236 lines
8.0 KiB
TypeScript
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();
|
|
}
|
|
} |