Files
go-service/cmd/server/web/js/layout.ts
2026-06-17 15:59:53 +03:00

236 lines
8.0 KiB
TypeScript

// layout.ts
let resizer: HTMLElement | null = null;
let leftPanel: HTMLElement | null = null;
let rightPanel: HTMLElement | null = null;
let isOpen: boolean = false;
let lastLeftWidth: number = 0;
let lastRightWidth: number = 0;
export function initResize(): void {
resizer = document.getElementById("dragBar");
leftPanel = document.getElementById("leftPanel");
rightPanel = document.getElementById("rightPanel");
if (!resizer || !leftPanel || !rightPanel) {
console.warn("[layout] Элементы не найдены");
return;
}
// Проверяем видимость при инициализации
updateResizerVisibility();
let isDragging = false;
// Обработчик начала перетаскивания
resizer.addEventListener("mousedown", (e: MouseEvent) => {
// Сохраняем ссылки на элементы в локальные переменные
const left = leftPanel;
const right = rightPanel;
// Проверяем, что элементы существуют
if (!left || !right) {
console.warn("[layout] Панели не найдены");
return;
}
// Проверяем, что правая панель видима
if (right.classList.contains("hidden")) {
return;
}
isDragging = true;
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
// Сохраняем начальные размеры
const startX = e.clientX;
const leftWidth = left.getBoundingClientRect().width;
const rightWidth = right.getBoundingClientRect().width;
const totalWidth = leftWidth + rightWidth;
function onMouseMove(moveEvent: MouseEvent): void {
if (!isDragging) return;
// Проверяем, что элементы всё ещё существуют
if (!left || !right) return;
const deltaX = moveEvent.clientX - startX;
let newLeftWidth = leftWidth + deltaX;
// Ограничиваем минимальные размеры
const minWidth = 200;
newLeftWidth = Math.max(minWidth, Math.min(totalWidth - minWidth, newLeftWidth));
const newRightWidth = totalWidth - newLeftWidth;
left.style.flex = `0 0 ${newLeftWidth}px`;
right.style.flex = `0 0 ${newRightWidth}px`;
// Сохраняем последние размеры
lastLeftWidth = newLeftWidth;
lastRightWidth = newRightWidth;
}
function onMouseUp(): void {
isDragging = false;
document.body.style.cursor = "";
document.body.style.userSelect = "";
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
}
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
});
// Обработчик изменения размера окна
window.addEventListener("resize", () => {
handleWindowResize();
});
}
/**
* Обработка изменения размера окна
*/
function handleWindowResize(): void {
if (!leftPanel || !rightPanel) return;
// Если правая панель видима, пересчитываем размеры
if (!rightPanel.classList.contains("hidden")) {
// Если есть сохраненные размеры, используем их пропорционально
if (lastLeftWidth > 0 && lastRightWidth > 0) {
const totalWidth = window.innerWidth;
const ratio = lastLeftWidth / (lastLeftWidth + lastRightWidth);
const newLeftWidth = totalWidth * ratio;
const newRightWidth = totalWidth - newLeftWidth;
// Ограничиваем минимальные размеры
const minWidth = 200;
const finalLeftWidth = Math.max(minWidth, Math.min(totalWidth - minWidth, newLeftWidth));
const finalRightWidth = totalWidth - finalLeftWidth;
leftPanel.style.flex = `0 0 ${finalLeftWidth}px`;
rightPanel.style.flex = `0 0 ${finalRightWidth}px`;
lastLeftWidth = finalLeftWidth;
lastRightWidth = finalRightWidth;
} else {
// Если нет сохраненных размеров, делим пополам
const totalWidth = window.innerWidth;
const halfWidth = totalWidth / 2;
leftPanel.style.flex = `0 0 ${halfWidth}px`;
rightPanel.style.flex = `0 0 ${halfWidth}px`;
lastLeftWidth = halfWidth;
lastRightWidth = halfWidth;
}
}
}
/**
* Обновление видимости разделителя в зависимости от состояния правой панели
*/
export function updateResizerVisibility(): void {
if (!resizer || !rightPanel) return;
// Если правая панель скрыта - скрываем разделитель
if (rightPanel.classList.contains("hidden")) {
resizer.classList.add("hidden");
} else {
resizer.classList.remove("hidden");
}
}
/**
* Открыть правую панель
*/
export function openRightPanel(): void {
if (!leftPanel || !rightPanel) return;
// Убираем класс hidden
rightPanel.classList.remove("hidden");
// Обновляем разделитель
updateResizerVisibility();
// Устанавливаем размеры
const totalWidth = window.innerWidth;
const halfWidth = totalWidth / 2;
// Проверяем, есть ли сохраненные размеры
if (lastLeftWidth > 0 && lastRightWidth > 0) {
// Используем сохраненные пропорции
const ratio = lastLeftWidth / (lastLeftWidth + lastRightWidth);
const newLeftWidth = totalWidth * ratio;
const newRightWidth = totalWidth - newLeftWidth;
const minWidth = 200;
const finalLeftWidth = Math.max(minWidth, Math.min(totalWidth - minWidth, newLeftWidth));
const finalRightWidth = totalWidth - finalLeftWidth;
leftPanel.style.flex = `0 0 ${finalLeftWidth}px`;
rightPanel.style.flex = `0 0 ${finalRightWidth}px`;
lastLeftWidth = finalLeftWidth;
lastRightWidth = finalRightWidth;
} else {
// Делим пополам
leftPanel.style.flex = `0 0 ${halfWidth}px`;
rightPanel.style.flex = `0 0 ${halfWidth}px`;
lastLeftWidth = halfWidth;
lastRightWidth = halfWidth;
}
}
/**
* Закрыть правую панель
*/
export function closeRightPanel(): void {
if (!rightPanel) return;
// Добавляем класс hidden
rightPanel.classList.add("hidden");
// Обновляем разделитель
updateResizerVisibility();
// Сбрасываем flex для левой панели
if (leftPanel) {
leftPanel.style.flex = "1 1 auto";
}
}
// ============================================
// ПУБЛИЧНЫЕ ФУНКЦИИ (используются в других модулях)
// ============================================
/**
* Установить видимость разделителя вручную
* @param visible - показывать или скрыть
*/
// noinspection JSUnusedGlobalSymbols
export function setResizerVisible(visible: boolean): void {
if (resizer) {
if (visible) {
resizer.classList.remove("hidden");
} else {
resizer.classList.add("hidden");
}
}
}
/**
* Переключить состояние правой панели
*/
// noinspection JSUnusedGlobalSymbols
export function toggleRightPanel(): void {
if (!rightPanel) return;
if (rightPanel.classList.contains("hidden")) {
openRightPanel();
} else {
closeRightPanel();
}
}