feat(11-03): responsive shells and storage rows

- App.vue: mobile header with hamburger button; slide-in overlay drawer
  with Teleport backdrop, translate-x-0/-translate-x-full transition;
  drawer state owned by App.vue (D-04/D-05); route-change auto-close
- AdminLayout.vue: matching responsive treatment — hamburger, backdrop,
  drawer, route-change close (RESP-05)
- StorageBrowser.vue: responsive grid templates
  (mobile: grid-cols-[2rem_1fr_6rem], sm: +modified, md: all 5 cols);
  Size hidden below md, Modified hidden below sm; action buttons get
  min-w-[36px] min-h-[36px] touch targets (RESP-02, RESP-03)
- Tests: drawer open/close/backdrop-close/route-change behaviour;
  admin drawer; responsive column and touch-target class assertions
This commit is contained in:
curo1305
2026-06-16 21:22:59 +02:00
parent 83cdf28231
commit d914761120
5 changed files with 332 additions and 18 deletions
+48 -2
View File
@@ -1,7 +1,41 @@
<template>
<div class="flex h-screen overflow-hidden">
<AdminSidebar />
<main class="flex-1 overflow-y-auto">
<!-- Mobile-only top header with hamburger (admin) -->
<header class="lg:hidden fixed top-0 left-0 right-0 z-30 flex items-center gap-3 px-4 py-3 bg-white border-b border-gray-200">
<button
@click="drawerOpen = true"
aria-label="Open admin navigation"
data-test="admin-hamburger-btn"
class="p-2 rounded-lg text-gray-500 hover:bg-gray-100 hover:text-gray-700 transition-colors"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<span class="text-base font-bold text-indigo-600 tracking-tight">DocuVault</span>
<span class="text-xs font-semibold text-indigo-500">Admin</span>
</header>
<!-- Drawer backdrop (mobile only) teleported to body to guarantee stacking -->
<Teleport to="body">
<div
v-if="drawerOpen"
class="lg:hidden fixed inset-0 z-40 bg-black/40"
data-test="admin-drawer-backdrop"
@click="drawerOpen = false"
></div>
</Teleport>
<!-- Sidebar: always visible on lg+; slides in as overlay drawer below lg -->
<div
class="fixed inset-y-0 left-0 z-50 transition-transform duration-200 ease-in-out lg:static lg:z-auto lg:translate-x-0 shrink-0"
:class="drawerOpen ? 'translate-x-0' : '-translate-x-full'"
data-test="admin-sidebar-wrapper"
>
<AdminSidebar />
</div>
<main class="flex-1 overflow-y-auto pt-[53px] lg:pt-0">
<div class="p-8 max-w-5xl mx-auto">
<router-view />
</div>
@@ -10,5 +44,17 @@
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import AdminSidebar from '../components/admin/AdminSidebar.vue'
// Drawer state owned by AdminLayout (per D-04/D-05 — not in AdminSidebar)
const drawerOpen = ref(false)
const route = useRoute()
// Close drawer on route change (navigation tap closes the drawer automatically)
watch(() => route.fullPath, () => {
drawerOpen.value = false
})
</script>