mobile drawer + responsive polish

This commit is contained in:
Dinarassan 2026-05-22 14:39:02 +05:00
parent 276d188637
commit 972d2be0d0

19
app.js
View File

@ -27,6 +27,7 @@ const CLIENT_STATUSES = ['active', 'on_hold', 'archived'];
let STATE = loadState() ?? clone(window.SEED); let STATE = loadState() ?? clone(window.SEED);
let MODAL = null; // { title, body, footer } object let MODAL = null; // { title, body, footer } object
let DROPDOWN = null; // 'notifications' | null let DROPDOWN = null; // 'notifications' | null
let SIDEBAR_OPEN = false; // mobile drawer state
function clone(o) { return JSON.parse(JSON.stringify(o)); } function clone(o) { return JSON.parse(JSON.stringify(o)); }
function loadState() { function loadState() {
@ -151,7 +152,7 @@ function parseHash() {
const query = Object.fromEntries(new URLSearchParams(queryStr || '')); const query = Object.fromEntries(new URLSearchParams(queryStr || ''));
return { path: '/' + segments.join('/'), segments, query }; return { path: '/' + segments.join('/'), segments, query };
} }
function navigate(hash) { location.hash = hash; } function navigate(hash) { SIDEBAR_OPEN = false; location.hash = hash; }
window.addEventListener('hashchange', render); window.addEventListener('hashchange', render);
window.addEventListener('DOMContentLoaded', render); window.addEventListener('DOMContentLoaded', render);
@ -165,7 +166,12 @@ function render() {
} }
const route = parseHash(); const route = parseHash();
root.innerHTML = root.innerHTML =
`<div class="shell">${renderSidebar(route)}${renderTopbar(route)}<main class="main">${renderMain(route)}</main></div>` + `<div class="shell ${SIDEBAR_OPEN ? 'sidebar-open' : ''}">` +
renderSidebar(route) +
renderTopbar(route) +
`<main class="main">${renderMain(route)}</main>` +
(SIDEBAR_OPEN ? '<div class="sidebar-backdrop" data-action="close-sidebar"></div>' : '') +
`</div>` +
(MODAL ? renderModalEl() : ''); (MODAL ? renderModalEl() : '');
attachGlobalListeners(); attachGlobalListeners();
} }
@ -243,6 +249,7 @@ function renderTopbar(route) {
const unread = unreadNotifications().length; const unread = unreadNotifications().length;
return ` return `
<header class="topbar"> <header class="topbar">
<button class="icon-button mobile-menu-btn" data-action="toggle-sidebar" aria-label="Меню">☰</button>
<form class="topbar-search" data-form="search"> <form class="topbar-search" data-form="search">
<input type="search" name="q" placeholder="Глобальный поиск…" value="${esc(q)}" /> <input type="search" name="q" placeholder="Глобальный поиск…" value="${esc(q)}" />
</form> </form>
@ -1142,6 +1149,14 @@ function bindDelegatedListeners() {
apply((s) => { s.currentUserId = el.dataset.userId; }); apply((s) => { s.currentUserId = el.dataset.userId; });
location.hash = '#/'; location.hash = '#/';
break; break;
case 'toggle-sidebar':
SIDEBAR_OPEN = !SIDEBAR_OPEN;
render();
break;
case 'close-sidebar':
SIDEBAR_OPEN = false;
render();
break;
case 'logout': case 'logout':
apply((s) => { s.currentUserId = null; }); apply((s) => { s.currentUserId = null; });
location.hash = ''; location.hash = '';