UI: адаптив, журнал, спиннеры; docs: README и api_routes
- Навигация: выезжающее меню при узком экране (nav-mobile.js) - Журнал: карточки <620px, компактная пагинация, время в две строки <920px - Создание кластера: оверлей загрузки, инкрементальное обновление таблицы заданий - Документация: полноэкранный спиннер при загрузке и навигации - Главная: масштабирование CTA, статистика 2 колонки <520px, донаты перенос <710px - README: env.example, новые фичи UI, автор в конце файла - api_routes: маршрут /cluster-create, спиннеры, шаблоны; автор в конце - env.example: автор перенесён в конец файла
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Адаптивное меню шапки: выезжающая панель при ширине viewport < 920px.
|
||||
* Закрытие по клику на подложку, Escape, переходу на другую страницу и при расширении окна.
|
||||
*
|
||||
* Автор: Сергей Антропов
|
||||
* Сайт: https://devops.org.ru
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var MQ = "(max-width: 919px)";
|
||||
var OPEN_CLASS = "top-nav-actions--open";
|
||||
var BODY_CLASS = "nav-mobile-open";
|
||||
var TOGGLE_OPEN_CLASS = "nav-menu-toggle--open";
|
||||
|
||||
function mqMobile() {
|
||||
return window.matchMedia(MQ).matches;
|
||||
}
|
||||
|
||||
/** Закрывает выпадающее меню API (дублирует логику из base.html, чтобы не дублировать DOM-события). */
|
||||
function closeApiDropdown() {
|
||||
var dd = document.querySelector(".nav-dropdown");
|
||||
if (!dd) return;
|
||||
var btn = document.getElementById("nav-api-trigger");
|
||||
var menu = document.getElementById("nav-api-menu");
|
||||
dd.classList.remove("nav-dropdown--open");
|
||||
if (btn) btn.setAttribute("aria-expanded", "false");
|
||||
if (menu) menu.setAttribute("hidden", "");
|
||||
}
|
||||
|
||||
function getEls() {
|
||||
return {
|
||||
toggle: document.getElementById("nav-menu-toggle"),
|
||||
drawer: document.getElementById("top-nav-drawer"),
|
||||
backdrop: document.getElementById("nav-menu-backdrop"),
|
||||
};
|
||||
}
|
||||
|
||||
function setOpen(open) {
|
||||
var els = getEls();
|
||||
if (!els.toggle || !els.drawer || !els.backdrop) return;
|
||||
|
||||
els.toggle.setAttribute("aria-expanded", open ? "true" : "false");
|
||||
els.toggle.setAttribute(
|
||||
"aria-label",
|
||||
open ? "Закрыть меню разделов" : "Открыть меню разделов",
|
||||
);
|
||||
els.toggle.classList.toggle(TOGGLE_OPEN_CLASS, open);
|
||||
els.drawer.classList.toggle(OPEN_CLASS, open);
|
||||
|
||||
if (open) {
|
||||
closeApiDropdown();
|
||||
els.backdrop.removeAttribute("hidden");
|
||||
els.backdrop.setAttribute("aria-hidden", "false");
|
||||
document.body.classList.add(BODY_CLASS);
|
||||
var closeBtn = document.getElementById("nav-drawer-close");
|
||||
if (closeBtn && mqMobile()) {
|
||||
try {
|
||||
closeBtn.focus();
|
||||
} catch (e) {
|
||||
/* focus не всегда доступен */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
els.backdrop.setAttribute("hidden", "");
|
||||
els.backdrop.setAttribute("aria-hidden", "true");
|
||||
document.body.classList.remove(BODY_CLASS);
|
||||
closeApiDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
var els = getEls();
|
||||
if (!els.drawer) return;
|
||||
setOpen(!els.drawer.classList.contains(OPEN_CLASS));
|
||||
}
|
||||
|
||||
function closeIfMobile() {
|
||||
if (mqMobile()) setOpen(false);
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
if (!mqMobile()) setOpen(false);
|
||||
}
|
||||
|
||||
function init() {
|
||||
var els = getEls();
|
||||
if (!els.toggle || !els.drawer || !els.backdrop) return;
|
||||
|
||||
els.toggle.addEventListener("click", function (e) {
|
||||
e.stopPropagation();
|
||||
if (!mqMobile()) return;
|
||||
toggle();
|
||||
});
|
||||
|
||||
els.backdrop.addEventListener("click", function () {
|
||||
closeIfMobile();
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Escape") closeIfMobile();
|
||||
});
|
||||
|
||||
window.addEventListener("resize", onResize);
|
||||
|
||||
/* Закрытие при уходе со страницы по ссылке внутри панели */
|
||||
els.drawer.addEventListener("click", function (e) {
|
||||
var a = e.target.closest("a[href]");
|
||||
if (!a || !mqMobile()) return;
|
||||
if (a.getAttribute("href") === "#" || a.getAttribute("href") === "") return;
|
||||
if (a.getAttribute("target") === "_blank") return;
|
||||
/* data-open-window открывает новое окно — страница не перезагружается */
|
||||
if (a.hasAttribute("data-open-window")) return;
|
||||
setOpen(false);
|
||||
});
|
||||
|
||||
var closeDrawer = document.getElementById("nav-drawer-close");
|
||||
if (closeDrawer) {
|
||||
closeDrawer.addEventListener("click", function (e) {
|
||||
e.stopPropagation();
|
||||
closeIfMobile();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user