import { CdkMenu, CdkMenuItem, CdkMenuTrigger } from '@angular/cdk/menu'; import { ChangeDetectionStrategy, Component, ViewEncapsulation, input, output, } from '@angular/core'; import { RouterLink } from '@angular/router'; import { Icon, type IconName } from '../icon/icon'; /** * Shape of a single row in the user menu — Profile, Settings, * cross-app links, etc. Sign out is NOT modelled here; it is always * rendered as the last row in a separate group and emits the * dedicated `signOut` output. * * Use `routerLink` for internal navigation (Angular `RouterLink`), * `href` for cross-origin links (other apps, external URLs). At most * one of the two should be set. */ export interface UserMenuItem { readonly label: string; readonly icon?: IconName; readonly routerLink?: string; readonly href?: string; readonly disabled?: boolean; /** Right-aligned chip — e.g. `"Soon"` for not-yet-shipped entries. */ readonly badge?: string; } /** * Authenticated user menu — the gitea/github-style avatar dropdown * docked at the top right of both `portal-shell` and `portal-admin` * headers. Rendered only when the session is `authenticated`; the * loading / anonymous / error widgets stay in each app's header * because they vary by surface (search bar, sign-up button, etc.). * * The component is layout-only: caller passes the principal * (`displayName`, `username`, `initials`) plus a list of `items` * for the navigation rows, and listens for `signOut` to actually * destroy the session. * * Keyboard nav (arrow keys, Home/End, Escape, type-ahead) is * provided by `@angular/cdk/menu` — same primitives the * `ThemeSwitcher` and `LocaleSwitcher` already use, kept consistent * to land familiar interactions across the chrome. */ @Component({ selector: 'lib-user-menu', imports: [CdkMenu, CdkMenuItem, CdkMenuTrigger, RouterLink, Icon], templateUrl: './user-menu.html', styleUrl: './user-menu.scss', changeDetection: ChangeDetectionStrategy.OnPush, // CDK Menu opens its panel in an overlay portal outside this // component's host. Encapsulated styles would not reach the // overlay; the BEM-prefixed class names below keep selectors // specific enough to avoid leakage. Same pattern as // `LocaleSwitcher` / `ThemeSwitcher`. encapsulation: ViewEncapsulation.None, }) export class UserMenu { readonly displayName = input.required(); readonly username = input.required(); readonly initials = input.required(); readonly items = input.required(); /** Localised label for the "Signed in as {username}" panel header. */ readonly signedInAsLabel = input('Signed in as'); /** Localised label for the trailing Sign out row. */ readonly signOutLabel = input('Sign out'); /** Localised aria-label for the avatar trigger button. */ readonly triggerAriaLabel = input('User menu'); readonly signOut = output(); }