feat: polish #13

Open
a.antonov wants to merge 8 commits from polish into components
Showing only changes of commit 7a2278a42e - Show all commits

View file

@ -1,80 +1,147 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/stores';
import {
Collapse,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
Nav,
NavItem,
NavLink,
Navbar,
NavbarBrand,
NavbarToggler,
} from '@sveltestrap/sveltestrap';
import { authStore } from '$auth'; import { authStore } from '$auth';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { t } from '$i18n'; import { t } from '$i18n';
// Auth is already refreshed by the root layout before this mounts. let isNavOpen = $state(false);
let isOpen = $state(false); let isDropdownOpen = $state(false);
async function handleLogout() { async function handleLogout() {
await authStore.logout(); await authStore.logout();
goto('/'); await goto('/');
}
function closeAll() {
isNavOpen = false;
isDropdownOpen = false;
} }
</script> </script>
<Navbar color="light" light expand="lg" fixed="top" class="custom-navbar border-bottom"> <svelte:window onclick={() => { if (isDropdownOpen) isDropdownOpen = false; }} />
<NavbarBrand href="/" class="nav-full-height">
<img src="/logo.svg" alt="Logo" height="34" class="d-inline-block align-text-top" /> <style>
</NavbarBrand> /* Stretch the container chain so every nav item reaches the full navbar height */
<NavbarToggler on:click={() => (isOpen = !isOpen)} /> .container-fluid {
<Collapse {isOpen} navbar expand="lg"> height: 100%;
<Nav class="me-auto mb-lg-0" navbar> align-items: stretch;
<NavItem> }
<NavLink
href="/predict" .navbar-collapse {
class="nav-full-height border border-top-0" align-self: stretch;
active={$page.url.pathname === '/predict'}> align-items: stretch;
{$t('nav.predict')} }
</NavLink>
</NavItem> .navbar-nav {
<NavItem> align-self: stretch;
<NavLink align-items: stretch;
href="/track" }
class="nav-full-height border border-top-0"
active={$page.url.pathname === '/track'}> .nav-item {
{$t('nav.track')} display: flex;
</NavLink> align-items: stretch;
</NavItem> }
</Nav>
<Nav navbar> /* Normalize <button> to match <a> nav-links exactly */
{#if $authStore.status === 'authenticated' && $authStore.username} button.nav-link {
<Dropdown nav inNavbar> appearance: none;
<DropdownToggle nav caret class="nav-full-height border border-top-0"> -webkit-appearance: none;
{$authStore.username} color: inherit;
</DropdownToggle> cursor: pointer;
<DropdownMenu end> border-radius: 0;
<DropdownItem href="/user/account">{$t('nav.account')}</DropdownItem> font-size: inherit;
<DropdownItem href="/user/templates">{$t('nav.scenarios')}</DropdownItem> font-family: inherit;
<DropdownItem href="/user/predictions">{$t('nav.predictionHistory')}</DropdownItem> background-color: white;
<DropdownItem href="/user/flights">{$t('nav.trackingHistory')}</DropdownItem> }
<DropdownItem divider />
<DropdownItem on:click={handleLogout}>{$t('nav.logout')}</DropdownItem> button.nav-link:hover {
</DropdownMenu> color: white !important;
</Dropdown> background-color: var(--bs-primary);
{:else if $authStore.status === 'anonymous'} }
<NavItem>
<NavLink /* Keep dropdown within viewport — clip vertically, guard horizontal edge */
href="/login" .dropdown-menu {
class="nav-full-height border border-top-0" max-height: calc(100vh - var(--navbar-height) - 4px);
active={$page.url.pathname === '/login'}> overflow-y: auto;
{$t('nav.login')} right: 0;
</NavLink> left: auto;
</NavItem> max-width: calc(100vw - 1.5rem);
{/if} }
</Nav> </style>
</Collapse>
</Navbar> <nav class="navbar navbar-expand-lg navbar-light fixed-top custom-navbar border-bottom">
<div class="container-fluid px-3">
<a class="navbar-brand nav-full-height" href="/">
<img src="/logo.svg" alt="Logo" height="34" class="d-inline-block align-text-top" />
</a>
<button
type="button"
class="navbar-toggler"
aria-controls="mainNav"
aria-expanded={isNavOpen}
aria-label="Toggle navigation"
onclick={() => (isNavOpen = !isNavOpen)}>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" class:show={isNavOpen} id="mainNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a
href="/predict"
class="nav-link nav-full-height border border-top-0"
class:active={$page.url.pathname === '/predict'}
onclick={closeAll}>
{$t('nav.predict')}
</a>
</li>
<li class="nav-item">
<a
href="/track"
class="nav-link nav-full-height border border-top-0"
class:active={$page.url.pathname === '/track'}
onclick={closeAll}>
{$t('nav.track')}
</a>
</li>
</ul>
<ul class="navbar-nav">
{#if $authStore.status === 'authenticated' && $authStore.username}
<li class="nav-item dropdown" class:show={isDropdownOpen}>
<button
type="button"
class="nav-link nav-full-height border border-top-0 dropdown-toggle"
aria-expanded={isDropdownOpen}
onclick={(e) => { e.stopPropagation(); isDropdownOpen = !isDropdownOpen; }}>
{$authStore.username}
</button>
<ul class="dropdown-menu dropdown-menu-end" class:show={isDropdownOpen}>
<li><a class="dropdown-item" href="/user/account" onclick={closeAll}>{$t('nav.account')}</a></li>
<li><a class="dropdown-item" href="/user/templates" onclick={closeAll}>{$t('nav.scenarios')}</a></li>
<li><a class="dropdown-item" href="/user/predictions" onclick={closeAll}>{$t('nav.predictionHistory')}</a></li>
<li><a class="dropdown-item" href="/user/flights" onclick={closeAll}>{$t('nav.trackingHistory')}</a></li>
<li><hr class="dropdown-divider" /></li>
<li>
<button type="button" class="dropdown-item" onclick={handleLogout}>
{$t('nav.logout')}
</button>
</li>
</ul>
</li>
{:else if $authStore.status === 'anonymous'}
<li class="nav-item">
<a
href="/login"
class="nav-link nav-full-height border border-top-0"
class:active={$page.url.pathname === '/login'}
onclick={closeAll}>
{$t('nav.login')}
</a>
</li>
{/if}
</ul>
</div>
</div>
</nav>