feat: responsive mobile UI/UX — hamburger menu, touch support, adaptive layout
- CSS: 3 breakpoints (480px / 768px / 1024px) avec mobile-first design - Hamburger button dans la topbar (visible ≤768px) - Sidebar slide-in avec overlay + bouton close - Kanban scroll-snap columns full-width sur mobile - Tables scrollables horizontalement - Touch targets 44px minimum - Swipe-left to close sidebar - Modal full-width sur ≤480px - Topbar simplifiée sur mobile (cacher breadcrumbs center) - Filter/toolbar wrap + scroll - Donut/team-load redimensionnés - app.js: touch events, htmx sidebar close, modal body scroll lock
This commit is contained in:
+38
-8
@@ -12,6 +12,12 @@
|
||||
<body hx-headers='{"X-CSRF-Token":"__CSRF_PLACEHOLDER__"}'>
|
||||
<div class="app-layout" x-data="appState()">
|
||||
|
||||
<!-- ═══════════ MOBILE SIDEBAR OVERLAY ═══════════ -->
|
||||
<div class="sidebar-overlay"
|
||||
:class="{ visible: mobileSidebarOpen }"
|
||||
@click="mobileSidebarOpen = false; sidebarCollapsed = true;">
|
||||
</div>
|
||||
|
||||
{# ─── Recursive tree macro ─── #}
|
||||
{% macro render_tree_item(page, depth=0) %}
|
||||
{% set indent_px = depth * 16 %}
|
||||
@@ -49,15 +55,22 @@
|
||||
{% endmacro %}
|
||||
|
||||
<!-- ═══════════ SIDEBAR Notion ═══════════ -->
|
||||
<aside class="sidebar" :class="{ collapsed: sidebarCollapsed }" id="sidebar"
|
||||
<aside class="sidebar"
|
||||
:class="{ collapsed: sidebarCollapsed, 'mobile-open': mobileSidebarOpen }"
|
||||
id="sidebar"
|
||||
x-data x-init="$nextTick(() => { if(typeof initTreeSortable==='function') initTreeSortable(); })">
|
||||
<!-- Workspace header -->
|
||||
<div class="sidebar-workspace" @click="toggleWorkspaceMenu">
|
||||
<div class="workspace-info">
|
||||
<div class="workspace-avatar">{{ workspaceInitial }}</div>
|
||||
<span class="workspace-name">{{ workspaceName }}</span>
|
||||
<!-- Workspace header + mobile back -->
|
||||
<div style="display:flex;align-items:center;gap:4px;">
|
||||
<button class="sidebar-back-btn" @click="mobileSidebarOpen = false; sidebarCollapsed = true;" title="Fermer le menu">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
</button>
|
||||
<div class="sidebar-workspace" @click="toggleWorkspaceMenu" style="flex:1;">
|
||||
<div class="workspace-info">
|
||||
<div class="workspace-avatar">{{ workspaceInitial }}</div>
|
||||
<span class="workspace-name">{{ workspaceName }}</span>
|
||||
</div>
|
||||
<span class="workspace-chevron">▾</span>
|
||||
</div>
|
||||
<span class="workspace-chevron">▾</span>
|
||||
</div>
|
||||
|
||||
<!-- Navigation icons row -->
|
||||
@@ -305,6 +318,10 @@
|
||||
<!-- Topbar -->
|
||||
{% block topbar %}
|
||||
<header class="topbar">
|
||||
<!-- Mobile hamburger -->
|
||||
<button class="hamburger-btn" @click="mobileSidebarOpen = true; sidebarCollapsed = false;" title="Menu">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
|
||||
</button>
|
||||
<div class="topbar-left">
|
||||
<div class="topbar-breadcrumb">
|
||||
<span x-text="workspaceName"></span>
|
||||
@@ -442,6 +459,7 @@
|
||||
function appState() {
|
||||
return {
|
||||
sidebarCollapsed: false,
|
||||
mobileSidebarOpen: false,
|
||||
showInvite: true,
|
||||
workspaceName: '{{ workspace_name }}',
|
||||
workspaceInitial: '{{ workspace_initial }}',
|
||||
@@ -461,7 +479,19 @@
|
||||
// ── Sections collapsible ──
|
||||
sectionsOpen: { recents: true, private: true, meetings: true, favorites: true, agents: true, shared: true, apps: true },
|
||||
toggleSection(key) { this.sectionsOpen[key] = !this.sectionsOpen[key]; this.sectionsOpen = Object.assign({}, this.sectionsOpen); },
|
||||
toggleWorkspaceMenu() { this.sidebarCollapsed = !this.sidebarCollapsed; },
|
||||
toggleWorkspaceMenu() {
|
||||
if (window.innerWidth <= 768) {
|
||||
this.mobileSidebarOpen = !this.mobileSidebarOpen;
|
||||
this.sidebarCollapsed = !this.mobileSidebarOpen;
|
||||
} else {
|
||||
this.sidebarCollapsed = !this.sidebarCollapsed;
|
||||
}
|
||||
},
|
||||
navigateTo(url) {
|
||||
this.mobileSidebarOpen = false;
|
||||
this.sidebarCollapsed = true;
|
||||
window.location.href = url;
|
||||
},
|
||||
openQuickFind() { alert('Quick Find — Ctrl+K'); },
|
||||
|
||||
// ── Tree state ──
|
||||
|
||||
Reference in New Issue
Block a user