m4: mobile nav — hamburger + slide-in drawer
Below sm the sidebar had no replacement (nav was inaccessible on phones). The sidebar aside is now a responsive drawer: fixed + off-canvas with a hamburger toggle and backdrop on mobile, static in-flow on sm+ (same markup, no duplication). Closes on nav-click, backdrop, Esc, or route change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -21,6 +21,7 @@ const ui = useUiStore();
|
||||
const managing = ref(false);
|
||||
const showShortcuts = ref(false);
|
||||
const paletteOpen = ref(false);
|
||||
const drawer = ref(false);
|
||||
const searchInput = ref<HTMLInputElement | null>(null);
|
||||
const searchText = ref(typeof route.query.q === "string" ? route.query.q : "");
|
||||
let searchTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
@@ -79,6 +80,10 @@ function onKeydown(e: KeyboardEvent) {
|
||||
showShortcuts.value = false;
|
||||
return;
|
||||
}
|
||||
if (e.key === "Escape" && drawer.value) {
|
||||
drawer.value = false;
|
||||
return;
|
||||
}
|
||||
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
||||
if (gPending) {
|
||||
gPending = false;
|
||||
@@ -148,6 +153,7 @@ watch(
|
||||
() => route.name,
|
||||
(name) => {
|
||||
if (name !== "search") searchText.value = "";
|
||||
drawer.value = false;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -163,6 +169,26 @@ async function signOut() {
|
||||
class="sticky top-0 z-20 border-b border-neutral-200 bg-neutral-50/90 backdrop-blur dark:border-neutral-800 dark:bg-neutral-950/90"
|
||||
>
|
||||
<div class="flex items-center gap-3 px-4 py-3">
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn sm:hidden"
|
||||
title="Menu"
|
||||
aria-label="Open menu"
|
||||
@click="drawer = true"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
>
|
||||
<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="flex shrink-0 items-center gap-2">
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-brand text-sm font-black text-neutral-900">
|
||||
TS
|
||||
@@ -203,8 +229,17 @@ async function signOut() {
|
||||
</header>
|
||||
|
||||
<div class="flex flex-1">
|
||||
<aside class="hidden w-56 shrink-0 border-r border-neutral-200 p-3 sm:block dark:border-neutral-800">
|
||||
<nav class="flex flex-col gap-0.5 text-sm">
|
||||
<div
|
||||
v-if="drawer"
|
||||
class="fixed inset-0 z-30 bg-black/40 sm:hidden"
|
||||
aria-hidden="true"
|
||||
@click="drawer = false"
|
||||
></div>
|
||||
<aside
|
||||
class="fixed inset-y-0 left-0 z-40 w-64 -translate-x-full overflow-y-auto border-r border-neutral-200 bg-neutral-50 p-3 transition-transform duration-200 sm:static sm:z-auto sm:w-56 sm:translate-x-0 dark:border-neutral-800 dark:bg-neutral-950"
|
||||
:class="drawer ? 'translate-x-0' : ''"
|
||||
>
|
||||
<nav class="flex flex-col gap-0.5 text-sm" @click="drawer = false">
|
||||
<RouterLink to="/" class="nav-link" :class="route.name === 'board' ? 'nav-link-active' : ''">
|
||||
<Icon name="note" /> Notes
|
||||
</RouterLink>
|
||||
|
||||
Reference in New Issue
Block a user