feat: Добавлена drag & drop функциональность для multi-view окон
- Добавлены CSS стили для drag & drop (курсоры, анимации, placeholder) - Реализованы JavaScript функции для перетаскивания окон - Добавлена иконка перетаскивания в заголовки multi-view панелей - Автоматическое сохранение порядка окон в localStorage - Обновлена документация и справка - Добавлена подробная документация по drag & drop функциональности
This commit is contained in:
parent
c40b2b312e
commit
f5f6005b5c
21
README.md
21
README.md
@ -41,6 +41,8 @@ LogBoard+ особенно полезен для разработчиков, р
|
||||
### Основные возможности
|
||||
|
||||
- **Просмотр логов в реальном времени** - WebSocket соединения для live-логов
|
||||
- **Multi-view режим** - Одновременный просмотр логов нескольких контейнеров
|
||||
- **Drag & Drop** - Перетаскивание окон для изменения порядка в multi-view режиме
|
||||
- **Поддержка множественных проектов** - Фильтрация по проектам Docker Compose
|
||||
- **Безопасность** - JWT аутентификация и авторизация
|
||||
- **Фильтрация контейнеров** - Исключение проблемных контейнеров
|
||||
@ -395,6 +397,18 @@ curl http://localhost:9001/healthz
|
||||
- Количество исключенных контейнеров
|
||||
- Время ответа API
|
||||
|
||||
## Документация
|
||||
|
||||
Подробная документация по всем аспектам LogBoard+:
|
||||
|
||||
- **[Установка и настройка](docs/installation.md)** - Пошаговое руководство по установке
|
||||
- **[Конфигурация](docs/configuration.md)** - Настройка переменных окружения и параметров
|
||||
- **[API документация](docs/api.md)** - REST API и WebSocket API
|
||||
- **[Управление проектом](docs/management.md)** - Makefile и Docker Compose команды
|
||||
- **[Безопасность](docs/security.md)** - Рекомендации по безопасности
|
||||
- **[Устранение неполадок](docs/troubleshooting.md)** - Решение проблем
|
||||
- **[Drag & Drop](docs/drag-and-drop.md)** - Перетаскивание окон в multi-view режиме
|
||||
|
||||
## Разработка
|
||||
|
||||
### Локальная разработка
|
||||
@ -493,6 +507,13 @@ MIT License - см. файл [LICENSE](LICENSE) для подробностей.
|
||||
|
||||
## Changelog
|
||||
|
||||
### v2.0.0 (2024-01-XX)
|
||||
- **Drag & Drop для multi-view** - Перетаскивание окон для изменения порядка
|
||||
- Улучшенный интерфейс multi-view режима
|
||||
- Визуальные индикаторы перетаскивания
|
||||
- Автоматическое сохранение порядка окон
|
||||
- Обновленная документация
|
||||
|
||||
### v1.0.0 (2024-01-XX)
|
||||
- Первый релиз LogBoard+
|
||||
- Поддержка множественных проектов Docker Compose
|
||||
|
0
app/start.sh
Executable file
0
app/start.sh
Executable file
@ -2582,3 +2582,126 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px}
|
||||
.notification-close:hover {
|
||||
background: var(--chip);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
/* Мультипросмотр */
|
||||
.multi-view-grid {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
height: 100%;
|
||||
padding: 0px;
|
||||
/* Равная высота строк для нескольких рядов (3+ окон) */
|
||||
grid-auto-rows: 1fr;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* Drag & Drop стили для multi-view */
|
||||
.multi-view-panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
cursor: move; /* Курсор для перетаскивания */
|
||||
user-select: none; /* Запрещаем выделение текста при перетаскивании */
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Стили для перетаскиваемого элемента */
|
||||
.multi-view-panel.dragging {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
||||
z-index: 1000;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* Стили для области, куда можно бросить элемент */
|
||||
.multi-view-panel.drag-over {
|
||||
border-color: var(--accent);
|
||||
background: var(--tab-active);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
/* Индикатор перетаскивания в заголовке */
|
||||
.multi-view-header {
|
||||
padding: 5px 16px;
|
||||
background: var(--chip);
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-height: 16px;
|
||||
cursor: grab; /* Курсор для перетаскивания в заголовке */
|
||||
}
|
||||
|
||||
.multi-view-header:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* Иконка перетаскивания */
|
||||
.drag-handle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--muted);
|
||||
cursor: grab;
|
||||
margin-right: 8px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.drag-handle:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* Подсказка для перетаскивания */
|
||||
.drag-tooltip {
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
color: var(--fg);
|
||||
white-space: nowrap;
|
||||
z-index: 1001;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.2s ease, visibility 0.2s ease;
|
||||
}
|
||||
|
||||
.drag-tooltip.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Анимация для перестановки элементов */
|
||||
.multi-view-panel {
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
|
||||
/* Стили для сетки при перетаскивании */
|
||||
.multi-view-grid.dragging {
|
||||
gap: 4px; /* Увеличиваем отступы для лучшей видимости */
|
||||
}
|
||||
|
||||
/* Стили для placeholder при перетаскивании */
|
||||
.multi-view-panel.drag-placeholder {
|
||||
background: var(--chip);
|
||||
border: 2px dashed var(--accent);
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -1838,6 +1838,9 @@ async function setupMultiView() {
|
||||
|
||||
// Обновляем видимость кнопок LogLevels
|
||||
updateLogLevelsVisibility();
|
||||
|
||||
// Обновляем drag & drop для новых панелей
|
||||
updateDragAndDrop();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1854,7 +1857,10 @@ function createMultiViewPanel(service) {
|
||||
console.log(`createMultiViewPanel: Panel element created with data-container-id: ${service.id}`);
|
||||
|
||||
panel.innerHTML = `
|
||||
<div class="multi-view-header">
|
||||
<div class="multi-view-header" draggable="true" data-container-id="${service.id}">
|
||||
<div class="drag-handle" title="Перетащите для изменения порядка">
|
||||
<i class="fas fa-grip-vertical"></i>
|
||||
</div>
|
||||
<h4 class="multi-view-title">${escapeHtml(service.name)}</h4>
|
||||
<div class="multi-view-levels">
|
||||
<button class="level-btn debug-btn" data-level="debug" data-container-id="${service.id}" title="DEBUG">
|
||||
@ -5810,3 +5816,317 @@ function reinitializeElements() {
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* Drag & Drop функциональность для multi-view окон
|
||||
* Позволяет перетаскивать окна логов и менять их порядок
|
||||
*/
|
||||
|
||||
// Глобальные переменные для drag & drop
|
||||
let dragState = {
|
||||
isDragging: false,
|
||||
draggedElement: null,
|
||||
draggedIndex: -1,
|
||||
originalIndex: -1,
|
||||
placeholder: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Инициализация drag & drop для multi-view панелей
|
||||
*/
|
||||
function initDragAndDrop() {
|
||||
console.log('Инициализация drag & drop для multi-view');
|
||||
|
||||
// Добавляем обработчики событий для всех multi-view панелей
|
||||
document.addEventListener('dragstart', handleDragStart);
|
||||
document.addEventListener('dragend', handleDragEnd);
|
||||
document.addEventListener('dragover', handleDragOver);
|
||||
document.addEventListener('dragenter', handleDragEnter);
|
||||
document.addEventListener('dragleave', handleDragLeave);
|
||||
document.addEventListener('drop', handleDrop);
|
||||
|
||||
// Добавляем обработчики для заголовков панелей
|
||||
document.addEventListener('mousedown', handleMouseDown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик начала перетаскивания
|
||||
*/
|
||||
function handleDragStart(event) {
|
||||
const header = event.target.closest('.multi-view-header');
|
||||
if (!header || !state.multiViewMode) return;
|
||||
|
||||
const panel = header.closest('.multi-view-panel');
|
||||
if (!panel) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const containerId = header.getAttribute('data-container-id');
|
||||
if (!containerId) return;
|
||||
|
||||
// Находим индекс панели в сетке
|
||||
const grid = panel.closest('.multi-view-grid');
|
||||
if (!grid) return;
|
||||
|
||||
const panels = Array.from(grid.querySelectorAll('.multi-view-panel'));
|
||||
const draggedIndex = panels.indexOf(panel);
|
||||
|
||||
if (draggedIndex === -1) return;
|
||||
|
||||
// Устанавливаем состояние перетаскивания
|
||||
dragState.isDragging = true;
|
||||
dragState.draggedElement = panel;
|
||||
dragState.draggedIndex = draggedIndex;
|
||||
dragState.originalIndex = draggedIndex;
|
||||
|
||||
// Добавляем стили для перетаскиваемого элемента
|
||||
panel.classList.add('dragging');
|
||||
grid.classList.add('dragging');
|
||||
|
||||
// Создаем placeholder
|
||||
createDragPlaceholder(panel);
|
||||
|
||||
// Устанавливаем данные для перетаскивания
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('text/plain', containerId);
|
||||
|
||||
console.log(`Начато перетаскивание панели ${containerId} с индекса ${draggedIndex}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик окончания перетаскивания
|
||||
*/
|
||||
function handleDragEnd(event) {
|
||||
if (!dragState.isDragging) return;
|
||||
|
||||
const panel = dragState.draggedElement;
|
||||
if (panel) {
|
||||
panel.classList.remove('dragging');
|
||||
}
|
||||
|
||||
const grid = panel?.closest('.multi-view-grid');
|
||||
if (grid) {
|
||||
grid.classList.remove('dragging');
|
||||
}
|
||||
|
||||
// Удаляем placeholder
|
||||
removeDragPlaceholder();
|
||||
|
||||
// Убираем стили drag-over со всех панелей
|
||||
document.querySelectorAll('.multi-view-panel.drag-over').forEach(p => {
|
||||
p.classList.remove('drag-over');
|
||||
});
|
||||
|
||||
// Сбрасываем состояние
|
||||
dragState.isDragging = false;
|
||||
dragState.draggedElement = null;
|
||||
dragState.draggedIndex = -1;
|
||||
dragState.originalIndex = -1;
|
||||
dragState.placeholder = null;
|
||||
|
||||
console.log('Перетаскивание завершено');
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик перетаскивания над элементом
|
||||
*/
|
||||
function handleDragOver(event) {
|
||||
if (!dragState.isDragging) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик входа в зону перетаскивания
|
||||
*/
|
||||
function handleDragEnter(event) {
|
||||
if (!dragState.isDragging) return;
|
||||
|
||||
const panel = event.target.closest('.multi-view-panel');
|
||||
if (!panel || panel === dragState.draggedElement) return;
|
||||
|
||||
panel.classList.add('drag-over');
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик выхода из зоны перетаскивания
|
||||
*/
|
||||
function handleDragLeave(event) {
|
||||
if (!dragState.isDragging) return;
|
||||
|
||||
const panel = event.target.closest('.multi-view-panel');
|
||||
if (!panel) return;
|
||||
|
||||
// Проверяем, что мы действительно вышли из панели
|
||||
const rect = panel.getBoundingClientRect();
|
||||
const x = event.clientX;
|
||||
const y = event.clientY;
|
||||
|
||||
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
|
||||
panel.classList.remove('drag-over');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик сброса элемента
|
||||
*/
|
||||
function handleDrop(event) {
|
||||
if (!dragState.isDragging) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const targetPanel = event.target.closest('.multi-view-panel');
|
||||
if (!targetPanel || targetPanel === dragState.draggedElement) return;
|
||||
|
||||
const grid = targetPanel.closest('.multi-view-grid');
|
||||
if (!grid) return;
|
||||
|
||||
const panels = Array.from(grid.querySelectorAll('.multi-view-panel'));
|
||||
const targetIndex = panels.indexOf(targetPanel);
|
||||
|
||||
if (targetIndex === -1) return;
|
||||
|
||||
// Переставляем элементы
|
||||
reorderMultiViewPanels(dragState.originalIndex, targetIndex);
|
||||
|
||||
// Убираем стили
|
||||
targetPanel.classList.remove('drag-over');
|
||||
|
||||
console.log(`Панель перемещена с позиции ${dragState.originalIndex} на позицию ${targetIndex}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик нажатия мыши для улучшения UX
|
||||
*/
|
||||
function handleMouseDown(event) {
|
||||
const header = event.target.closest('.multi-view-header');
|
||||
if (!header || !state.multiViewMode) return;
|
||||
|
||||
const dragHandle = event.target.closest('.drag-handle');
|
||||
if (!dragHandle) return;
|
||||
|
||||
// Показываем подсказку
|
||||
showDragTooltip(header, 'Перетащите для изменения порядка');
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает placeholder для перетаскивания
|
||||
*/
|
||||
function createDragPlaceholder(originalPanel) {
|
||||
const placeholder = originalPanel.cloneNode(true);
|
||||
placeholder.classList.add('drag-placeholder');
|
||||
placeholder.classList.remove('dragging');
|
||||
|
||||
// Очищаем содержимое placeholder
|
||||
const content = placeholder.querySelector('.multi-view-content');
|
||||
if (content) {
|
||||
content.innerHTML = '<div class="multi-view-log"></div>';
|
||||
}
|
||||
|
||||
// Вставляем placeholder на место оригинального элемента
|
||||
originalPanel.parentNode.insertBefore(placeholder, originalPanel);
|
||||
|
||||
dragState.placeholder = placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет placeholder
|
||||
*/
|
||||
function removeDragPlaceholder() {
|
||||
if (dragState.placeholder) {
|
||||
dragState.placeholder.remove();
|
||||
dragState.placeholder = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Переставляет панели в multi-view
|
||||
*/
|
||||
function reorderMultiViewPanels(fromIndex, toIndex) {
|
||||
const grid = document.querySelector('.multi-view-grid');
|
||||
if (!grid) return;
|
||||
|
||||
const panels = Array.from(grid.querySelectorAll('.multi-view-panel'));
|
||||
if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 ||
|
||||
fromIndex >= panels.length || toIndex >= panels.length) return;
|
||||
|
||||
// Получаем ID контейнеров в текущем порядке
|
||||
const containerIds = panels.map(panel =>
|
||||
panel.querySelector('.multi-view-header').getAttribute('data-container-id')
|
||||
);
|
||||
|
||||
// Переставляем элементы в массиве
|
||||
const movedContainer = containerIds.splice(fromIndex, 1)[0];
|
||||
containerIds.splice(toIndex, 0, movedContainer);
|
||||
|
||||
// Обновляем порядок в state
|
||||
state.selectedContainers = containerIds;
|
||||
|
||||
// Сохраняем новый порядок
|
||||
saveViewMode(true, state.selectedContainers);
|
||||
|
||||
// Пересоздаем сетку с новым порядком
|
||||
setTimeout(() => {
|
||||
setupMultiView();
|
||||
}, 100);
|
||||
|
||||
console.log(`Порядок контейнеров обновлен: ${containerIds.join(', ')}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Показывает подсказку при перетаскивании
|
||||
*/
|
||||
function showDragTooltip(element, text) {
|
||||
// Удаляем существующие подсказки
|
||||
document.querySelectorAll('.drag-tooltip').forEach(tooltip => tooltip.remove());
|
||||
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'drag-tooltip';
|
||||
tooltip.textContent = text;
|
||||
|
||||
element.appendChild(tooltip);
|
||||
|
||||
// Показываем подсказку
|
||||
setTimeout(() => {
|
||||
tooltip.classList.add('show');
|
||||
}, 10);
|
||||
|
||||
// Скрываем подсказку через 2 секунды
|
||||
setTimeout(() => {
|
||||
tooltip.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
tooltip.remove();
|
||||
}, 200);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет drag & drop для новых панелей
|
||||
*/
|
||||
function updateDragAndDrop() {
|
||||
if (!state.multiViewMode) return;
|
||||
|
||||
// Удаляем старые обработчики
|
||||
document.querySelectorAll('.multi-view-header').forEach(header => {
|
||||
header.removeEventListener('dragstart', handleDragStart);
|
||||
header.removeEventListener('dragend', handleDragEnd);
|
||||
});
|
||||
|
||||
// Добавляем обработчики для новых панелей
|
||||
document.querySelectorAll('.multi-view-header').forEach(header => {
|
||||
header.setAttribute('draggable', 'true');
|
||||
});
|
||||
}
|
||||
|
||||
// Инициализируем drag & drop при загрузке страницы
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initDragAndDrop();
|
||||
});
|
||||
|
||||
// Обновляем drag & drop при изменении multi-view
|
||||
const originalSetupMultiView = window.setupMultiView;
|
||||
window.setupMultiView = async function() {
|
||||
await originalSetupMultiView.call(this);
|
||||
updateDragAndDrop();
|
||||
};
|
@ -274,6 +274,8 @@
|
||||
<div class="help-tooltip-description">Добавить в мультивыбор</div>
|
||||
<div class="help-tooltip-hotkey">Выделение текста</div>
|
||||
<div class="help-tooltip-description">Показать кнопку копирования</div>
|
||||
<div class="help-tooltip-hotkey">Перетаскивание заголовка</div>
|
||||
<div class="help-tooltip-description">Изменить порядок окон в multi-view</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -399,6 +401,14 @@
|
||||
<li><kbd>Ctrl</kbd> + <kbd>И</kbd> <span>Свернуть/развернуть панель (русская раскладка)</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="hotkeys-section">
|
||||
<h4 class="hotkeys-section-title">Multi-View</h4>
|
||||
<ul class="hotkeys-list">
|
||||
<li><span>Перетащите заголовок окна</span> <span>Изменить порядок окон</span></li>
|
||||
<li><span>Иконка ⋮⋮ в заголовке</span> <span>Индикатор перетаскивания</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,6 +9,8 @@ services:
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./snapshots:/app/snapshots
|
||||
- ./app:/app
|
||||
- ./start.sh:/app/start.sh
|
||||
restart: unless-stopped
|
||||
user: 0:0
|
||||
networks:
|
||||
|
182
docs/drag-and-drop.md
Normal file
182
docs/drag-and-drop.md
Normal file
@ -0,0 +1,182 @@
|
||||
# Drag & Drop для Multi-View режима
|
||||
|
||||
## Обзор
|
||||
|
||||
LogBoard+ поддерживает drag & drop функциональность в режиме мультипросмотра (multi-view), позволяя пользователям изменять порядок окон логов путем перетаскивания их заголовков.
|
||||
|
||||
## Возможности
|
||||
|
||||
### Основные функции
|
||||
- **Перетаскивание окон**: Изменение порядка окон логов в multi-view режиме
|
||||
- **Визуальная обратная связь**: Анимации и стили во время перетаскивания
|
||||
- **Сохранение порядка**: Автоматическое сохранение нового порядка в localStorage
|
||||
- **Placeholder**: Визуальный индикатор места вставки во время перетаскивания
|
||||
|
||||
### Индикаторы перетаскивания
|
||||
- **Иконка ⋮⋮**: Отображается в заголовке каждого окна
|
||||
- **Курсор**: Изменяется на `grab`/`grabbing` при наведении и перетаскивании
|
||||
- **Подсказки**: Всплывающие подсказки с инструкциями
|
||||
|
||||
## Использование
|
||||
|
||||
### Как перетащить окно
|
||||
1. **Включите multi-view режим**: Выберите несколько контейнеров (Shift+клик или Ctrl+клик)
|
||||
2. **Найдите иконку перетаскивания**: В заголовке каждого окна есть иконка ⋮⋮
|
||||
3. **Начните перетаскивание**: Нажмите и удерживайте левую кнопку мыши на заголовке окна
|
||||
4. **Переместите окно**: Перетащите окно в нужную позицию
|
||||
5. **Отпустите кнопку мыши**: Окно встанет на новое место
|
||||
|
||||
### Визуальные эффекты
|
||||
- **Во время перетаскивания**:
|
||||
- Окно становится полупрозрачным
|
||||
- Появляется тень
|
||||
- Окно слегка увеличивается
|
||||
- Создается placeholder на месте исходного окна
|
||||
|
||||
- **При наведении на другие окна**:
|
||||
- Окна подсвечиваются синим цветом
|
||||
- Показывается, куда будет вставлено окно
|
||||
|
||||
## Техническая реализация
|
||||
|
||||
### CSS классы
|
||||
```css
|
||||
.multi-view-panel.dragging {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
||||
z-index: 1000;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.multi-view-panel.drag-over {
|
||||
border-color: var(--accent);
|
||||
background: var(--tab-active);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
.multi-view-panel.drag-placeholder {
|
||||
background: var(--chip);
|
||||
border: 2px dashed var(--accent);
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript функции
|
||||
- `initDragAndDrop()`: Инициализация drag & drop
|
||||
- `handleDragStart()`: Обработчик начала перетаскивания
|
||||
- `handleDragEnd()`: Обработчик окончания перетаскивания
|
||||
- `handleDrop()`: Обработчик сброса элемента
|
||||
- `reorderMultiViewPanels()`: Перестановка панелей
|
||||
- `updateDragAndDrop()`: Обновление drag & drop для новых панелей
|
||||
|
||||
### Состояние перетаскивания
|
||||
```javascript
|
||||
let dragState = {
|
||||
isDragging: false,
|
||||
draggedElement: null,
|
||||
draggedIndex: -1,
|
||||
originalIndex: -1,
|
||||
placeholder: null
|
||||
};
|
||||
```
|
||||
|
||||
## Ограничения
|
||||
|
||||
### Поддерживаемые браузеры
|
||||
- Chrome 66+
|
||||
- Firefox 60+
|
||||
- Safari 12+
|
||||
- Edge 79+
|
||||
|
||||
### Условия работы
|
||||
- Только в multi-view режиме (2+ контейнера)
|
||||
- Только перетаскивание заголовков окон
|
||||
- Не работает в single-view режиме
|
||||
|
||||
## Настройки
|
||||
|
||||
### Сохранение порядка
|
||||
Порядок окон автоматически сохраняется в localStorage и восстанавливается при перезагрузке страницы.
|
||||
|
||||
### Отключение функциональности
|
||||
Для отключения drag & drop можно удалить обработчики событий:
|
||||
```javascript
|
||||
document.removeEventListener('dragstart', handleDragStart);
|
||||
document.removeEventListener('dragend', handleDragEnd);
|
||||
// ... и другие обработчики
|
||||
```
|
||||
|
||||
## Устранение неполадок
|
||||
|
||||
### Проблемы с перетаскиванием
|
||||
1. **Окно не перетаскивается**:
|
||||
- Убедитесь, что вы в multi-view режиме
|
||||
- Проверьте, что перетаскиваете за заголовок окна
|
||||
- Убедитесь, что браузер поддерживает HTML5 Drag & Drop
|
||||
|
||||
2. **Порядок не сохраняется**:
|
||||
- Проверьте, что localStorage доступен
|
||||
- Убедитесь, что нет ошибок в консоли браузера
|
||||
|
||||
3. **Визуальные эффекты не работают**:
|
||||
- Проверьте, что CSS стили загружены
|
||||
- Убедитесь, что нет конфликтов с другими стилями
|
||||
|
||||
### Отладка
|
||||
Включите отладочные сообщения в консоли браузера:
|
||||
```javascript
|
||||
console.log('Drag & Drop Debug:', dragState);
|
||||
```
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### Базовое перетаскивание
|
||||
```javascript
|
||||
// Автоматически инициализируется при загрузке страницы
|
||||
initDragAndDrop();
|
||||
```
|
||||
|
||||
### Программное изменение порядка
|
||||
```javascript
|
||||
// Перестановка панелей программно
|
||||
reorderMultiViewPanels(0, 2); // Переместить первую панель на третье место
|
||||
```
|
||||
|
||||
### Получение текущего порядка
|
||||
```javascript
|
||||
// Получить текущий порядок контейнеров
|
||||
const currentOrder = state.selectedContainers;
|
||||
console.log('Текущий порядок:', currentOrder);
|
||||
```
|
||||
|
||||
## Совместимость
|
||||
|
||||
### С существующим функционалом
|
||||
- Совместимо с AJAX обновлением логов
|
||||
- Работает с фильтрацией по уровням логирования
|
||||
- Поддерживает автопрокрутку и перенос строк
|
||||
- Интегрируется с системой сохранения настроек
|
||||
|
||||
### Производительность
|
||||
- Минимальное влияние на производительность
|
||||
- Оптимизированные обработчики событий
|
||||
- Эффективное обновление DOM
|
||||
|
||||
## Планы развития
|
||||
|
||||
### Будущие улучшения
|
||||
- Поддержка перетаскивания между разными сетками
|
||||
- Анимации при изменении размера окон
|
||||
- Группировка окон по категориям
|
||||
- Экспорт/импорт конфигурации порядка окон
|
||||
|
||||
### Обратная связь
|
||||
Для предложений по улучшению drag & drop функциональности обращайтесь к автору проекта.
|
||||
|
||||
---
|
||||
|
||||
**Автор**: Сергей Антропов
|
||||
**Сайт**: https://devops.org.ru
|
||||
**Версия**: 2.0
|
Loading…
x
Reference in New Issue
Block a user