feat(10-04): collapsible panel with flyout, user menu with logout, breadcrumbs and dark mode
- useSidebar: collapsed below 1100px (matchMedia) or by the admin's choice, persisted as a boolean under summer-admin.sidebar; the viewport never overwrites the stored choice - SectionPanel collapse and rail expand buttons; SectionFlyout (role menu) opens on hover, focus, Enter or ArrowDown on a rail item, closes on Esc or about 200 ms after leaving and returns focus to the rail item - Breadcrumbs with plugin, controller and record crumbs; UserMenu (Reka DropdownMenu) with initials, name, role and Wyloguj - useAuth.logout POSTs /auth/logout, clears user, navigation and settings and routes to login even when the call fails - applyColorScheme toggles .dark from prefers-color-scheme (no toggle, A6) - newest toast first, 200 ms fade and scale for dialogs and toasts - one relation-manager type constant in the registry - shell smoke tests; tests default to a desktop, light matchMedia
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, nextTick } from 'vue'
|
||||
import { PanelLeftClose } from '@lucide/vue'
|
||||
import { RouterLink, useRoute } from 'vue-router'
|
||||
import type { NavigationEntry } from '../../api/types'
|
||||
import { controllerIdFromPath, controllerPath } from '../../app/controllerRoutes'
|
||||
import { iconFor } from '../../app/icons'
|
||||
import { t } from '../../app/i18n'
|
||||
import { setCollapsed } from '../../state/useSidebar'
|
||||
|
||||
// Section panel (design: Navigation shell, level 2): the active plugin's
|
||||
// permitted side menu, 224px, with the plugin label and a "Zwiń menu" button
|
||||
// in its header. Collapsing hides it and leaves only the rail (D-06).
|
||||
|
||||
const props = defineProps<{ entry: NavigationEntry }>()
|
||||
const route = useRoute()
|
||||
@@ -20,6 +26,11 @@ const items = computed(() =>
|
||||
return [{ item, to, icon: iconFor(item.icon), current: currentController.value === item.controller }]
|
||||
}),
|
||||
)
|
||||
|
||||
function collapse(): void {
|
||||
setCollapsed(true)
|
||||
void nextTick(() => document.querySelector<HTMLElement>('[data-sidebar-expand]')?.focus())
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -27,8 +38,18 @@ const items = computed(() =>
|
||||
:aria-label="t('backend::lang.nav.sections')"
|
||||
class="flex w-panel shrink-0 flex-col gap-0.5 border-r border-border bg-surface px-3 py-3.5"
|
||||
>
|
||||
<div class="mb-3.5 flex h-10 items-center px-2">
|
||||
<span class="text-[16px] font-bold">{{ entry.label }}</span>
|
||||
<div class="mb-3.5 flex h-10 items-center justify-between gap-2 pl-2">
|
||||
<span class="truncate text-[16px] font-bold">{{ entry.label }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-sidebar-collapse
|
||||
:aria-label="t('backend::lang.nav.collapse')"
|
||||
:title="t('backend::lang.nav.collapse')"
|
||||
class="flex size-[30px] shrink-0 items-center justify-center rounded-tab text-muted transition-colors duration-150 ease-out hover:bg-hover hover:text-text"
|
||||
@click="collapse"
|
||||
>
|
||||
<PanelLeftClose :size="18" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<RouterLink
|
||||
v-for="link in items"
|
||||
@@ -36,7 +57,7 @@ const items = computed(() =>
|
||||
:to="link.to"
|
||||
:aria-current="link.current ? 'page' : undefined"
|
||||
:class="link.current ? 'bg-sel font-semibold text-text' : 'font-medium text-muted hover:bg-hover hover:text-text'"
|
||||
class="flex h-nav items-center gap-3 rounded-control px-3 no-underline"
|
||||
class="flex h-nav items-center gap-3 rounded-control px-3 no-underline transition-colors duration-150 ease-out"
|
||||
>
|
||||
<component :is="link.icon" :size="18" aria-hidden="true" />
|
||||
<span>{{ link.item.label }}</span>
|
||||
|
||||
Reference in New Issue
Block a user