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:
25
admin/src/app/theme.ts
Normal file
25
admin/src/app/theme.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// Colour scheme (D-06, flagged assumption A6): the admin follows the system
|
||||
// preference only, with no toggle. The .dark class on the document element
|
||||
// swaps the design's dark tokens in main.css; the sidebar stays dark in both
|
||||
// modes and gains its #222b40 right border in dark mode.
|
||||
export const DARK_QUERY = '(prefers-color-scheme: dark)'
|
||||
|
||||
/**
|
||||
* Applies the current preference and follows changes. Returns the
|
||||
* unsubscribe function.
|
||||
*/
|
||||
export function applyColorScheme(win: Window = window): () => void {
|
||||
const root = win.document.documentElement
|
||||
if (typeof win.matchMedia !== 'function') {
|
||||
root.classList.remove('dark')
|
||||
return () => undefined
|
||||
}
|
||||
const query = win.matchMedia(DARK_QUERY)
|
||||
const apply = (dark: boolean): void => {
|
||||
root.classList.toggle('dark', dark)
|
||||
}
|
||||
apply(query.matches)
|
||||
const onChange = (event: MediaQueryListEvent): void => apply(event.matches)
|
||||
query.addEventListener('change', onChange)
|
||||
return () => query.removeEventListener('change', onChange)
|
||||
}
|
||||
Reference in New Issue
Block a user