@lifecorner/angular-ui-kit 3.34.0 → 3.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -0
  2. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs +2 -2
  3. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs.map +1 -1
  4. package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-button.mjs +6 -6
  6. package/fesm2022/lifecorner-angular-ui-kit-button.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-card.mjs +82 -16
  8. package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +23 -4
  10. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +43 -5
  12. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs +30 -2
  14. package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
  16. package/fesm2022/lifecorner-angular-ui-kit-header.mjs +45 -8
  17. package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
  18. package/fesm2022/lifecorner-angular-ui-kit-hero.mjs +57 -5
  19. package/fesm2022/lifecorner-angular-ui-kit-hero.mjs.map +1 -1
  20. package/fesm2022/lifecorner-angular-ui-kit-internal-overlay.mjs +6 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-internal-overlay.mjs.map +1 -1
  22. package/fesm2022/lifecorner-angular-ui-kit-list.mjs +24 -5
  23. package/fesm2022/lifecorner-angular-ui-kit-list.mjs.map +1 -1
  24. package/fesm2022/lifecorner-angular-ui-kit-modal.mjs +2 -4
  25. package/fesm2022/lifecorner-angular-ui-kit-modal.mjs.map +1 -1
  26. package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs +41 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs.map +1 -1
  28. package/fesm2022/lifecorner-angular-ui-kit-stacked-bar-chart.mjs +47 -5
  29. package/fesm2022/lifecorner-angular-ui-kit-stacked-bar-chart.mjs.map +1 -1
  30. package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs +203 -0
  31. package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs.map +1 -0
  32. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +25 -4
  33. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  34. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs +3 -0
  35. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs.map +1 -1
  36. package/fesm2022/lifecorner-angular-ui-kit-toast.mjs.map +1 -1
  37. package/fesm2022/lifecorner-angular-ui-kit.mjs +1 -0
  38. package/package.json +5 -1
  39. package/src/styles/theme-contrast.spec.ts +74 -0
  40. package/types/lifecorner-angular-ui-kit-alert.d.ts +12 -2
  41. package/types/lifecorner-angular-ui-kit-bottom-nav.d.ts +5 -1
  42. package/types/lifecorner-angular-ui-kit-button.d.ts +7 -3
  43. package/types/lifecorner-angular-ui-kit-card.d.ts +51 -3
  44. package/types/lifecorner-angular-ui-kit-combobox.d.ts +36 -0
  45. package/types/lifecorner-angular-ui-kit-description-list.d.ts +40 -0
  46. package/types/lifecorner-angular-ui-kit-drawer.d.ts +37 -1
  47. package/types/lifecorner-angular-ui-kit-gallery.d.ts +3 -1
  48. package/types/lifecorner-angular-ui-kit-header.d.ts +44 -2
  49. package/types/lifecorner-angular-ui-kit-hero.d.ts +32 -0
  50. package/types/lifecorner-angular-ui-kit-internal-overlay.d.ts +9 -1
  51. package/types/lifecorner-angular-ui-kit-list.d.ts +17 -0
  52. package/types/lifecorner-angular-ui-kit-sidenav.d.ts +42 -1
  53. package/types/lifecorner-angular-ui-kit-stacked-bar-chart.d.ts +36 -0
  54. package/types/lifecorner-angular-ui-kit-state-strip.d.ts +107 -0
  55. package/types/lifecorner-angular-ui-kit-table.d.ts +37 -0
  56. package/types/lifecorner-angular-ui-kit-textarea.d.ts +17 -0
  57. package/types/lifecorner-angular-ui-kit-toast.d.ts +4 -2
  58. package/types/lifecorner-angular-ui-kit.d.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-header.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/header/header.component.ts","../../../../libs/angular-ui-kit/src/lib/header/header.component.html"],"sourcesContent":["import {\n AfterRenderRef,\n Component,\n ElementRef,\n Injector,\n afterNextRender,\n input,\n output,\n signal,\n computed,\n inject,\n viewChild,\n afterRenderEffect,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterModule } from '@angular/router';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { LogoComponent } from '@lifecorner/angular-ui-kit/logo';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_BARS_3, ICON_CHEVRON_DOWN, ICON_LOGOUT, ICON_MOON, ICON_SUN, ICON_USER } from '@lifecorner/angular-ui-kit/icons';\nimport { AvatarColor, AvatarComponent, AvatarFallback, ɵavatarInitials } from '@lifecorner/angular-ui-kit/avatar';\nimport { MenuComponent, MenuItem } from '@lifecorner/angular-ui-kit/menu';\nimport { ThemeService } from '@lifecorner/angular-ui-kit/theme';\nimport { nameWithText } from '@lifecorner/angular-ui-kit/internal/a11y';\n\n/** A link in the header's main navigation (`navItems`). */\nexport interface HeaderNavItem {\n /** Visible link text. */\n label: string;\n /** Plain URL — rendered as a native `href`, no router involved. */\n href: string;\n /** Marks the page the visitor is on (`aria-current=\"page\"` + underline). */\n current?: boolean;\n}\n\n/** Element used for the header title (`titleTag`). */\nexport type HeaderTitleTag = 'h1' | 'h2' | 'h3' | 'p' | 'span' | 'div';\n\n/**\n * Surface of the header: `default` is the bar on `--color-surface` with a\n * bottom border; `transparent` has no background and no border, for a header\n * that sits on the page (\"Weite\").\n */\nexport type HeaderVariant = 'default' | 'transparent';\n\n/**\n * What the account-menu button shows: `icon` (a user icon and a chevron) or\n * `avatar` (the person's initials avatar, `userName`, `userAvatarSrc`,\n * `userAvatarColor`, in a 48 px round button).\n */\nexport type HeaderUserMenuTrigger = 'icon' | 'avatar';\n\n/**\n * Where the context button sits up to 640 px (3.33): `inline` in the end\n * group, hidden up to 640 px (as before), or `below`, a row of its own under\n * the title. From 641 px both render it inline.\n */\nexport type HeaderContextPlacement = 'inline' | 'below';\n\n/**\n * AppHeaderComponent - Global application header for the LifeCorner shell\n *\n * Features:\n * - Clickable logo for home navigation\n * - Optional title and subtitle display\n * - User profile dropdown with avatar, name, email, optional Profile link, and Logout\n * - Optional theme toggle button in header. By default the button toggles the\n * `ThemeService` itself and additionally emits `themeToggleClick`, so the\n * handler must NOT toggle again (that would undo the switch). To own the\n * toggling in the app, set `[autoToggleTheme]=\"false\"` and toggle in the\n * handler.\n * - Hamburger menu toggle for mobile sidebar; bind `sidenavOpen` (and\n * `sidenavId`) so the toggle exposes `aria-expanded` / `aria-controls`.\n * - OnPush change detection for performance\n *\n * Public, prerendered sites (no router, no JavaScript needed):\n * - `homeHref` makes the brand a plain link (instead of `routerLink=\"/\"`).\n * - `navItems` render a labelled main navigation (`<nav aria-label><ul>` with\n * `aria-current=\"page\"` on the current item); below 1024 px it moves to a\n * second row and wraps.\n * - `[slot='end']` takes content at the end of the first row (e.g. a language\n * link), `showUserMenu` hides the account menu, `titleTag` picks the title\n * element (the page keeps its own `<h1>`), `sticky` can be switched off.\n * - The end content (slot, theme button, context, account menu) is one group:\n * when it doesn't fit beside the logo and the title's longest word (200 %\n * text on a phone), it takes the end of a second row instead of covering the\n * brand or widening the page (3.10.1, AS-31). The subtitle ends with \"…\".\n * App shell (\"Weite\", kit 3.3):\n * - `variant=\"transparent\"`: no background and no border, the header sits on\n * the page (pair it with `[sticky]=\"false\"`, a header without a surface\n * cannot stay over scrolling text).\n * - `userMenuTrigger=\"avatar\"`: the account button is the person's initials\n * avatar (`userAvatarColor` for the member colour, `userAvatarSrc` for a\n * photo, `userAvatarFallback=\"person\"` for a person symbol while there is\n * no name) in a 48 px round button; the icon trigger is 44 px high in every\n * density.\n * - The subtitle (e.g. a household name) stays on one line and ends with \"…\";\n * the full text is its `title` and stays in the accessible text. It is shown\n * as typed (no uppercase).\n * - The account menu is at most `min(18rem, 100vw − 2rem)` wide; the name and\n * email in its header wrap instead of being cut.\n * - `contextPlacement=\"below\"` (3.33): up to 640 px the context button\n * (`contextName`) takes a full-width row of its own under the title instead\n * of being hidden; from 641 px it sits in the end group as before.\n * - `focusContext()` (3.34): moves the focus to the context button after the\n * next render, e.g. after a choice in the sheet it opened (WCAG 2.4.3).\n * - Every built-in label is an input (`homeAriaLabel`, `hamburgerAriaLabel`,\n * `userMenuAriaLabel`, `navAriaLabel`, `profileLabel`, `logoutLabel`,\n * `themeToDarkAriaLabel`, `themeToLightAriaLabel`, `contextAriaLabel`);\n * the defaults stay English.\n * - The `ThemeService` (which persists to localStorage) is only created when\n * the theme button is shown.\n *\n * @example\n * ```html\n * <lc-header\n * [logo]=\"'/assets/logo.svg'\"\n * [title]=\"'LifeCorner'\"\n * [subtitle]=\"'User Profile'\"\n * [userName]=\"'John Doe'\"\n * [userEmail]=\"'user@example.com'\"\n * [showHamburger]=\"true\"\n * [sidenavOpen]=\"sidenavOpen\"\n * sidenavId=\"app-sidenav\"\n * [showThemeButton]=\"true\"\n * [showProfileMenuItem]=\"true\"\n * (hamburgerClick)=\"toggleSidebar()\"\n * (themeToggleClick)=\"trackThemeToggle()\"\n * (profileClick)=\"navigateToProfile()\"\n * (logoutClick)=\"handleLogout()\"\n * />\n *\n * <!-- App-controlled theme: the header only reports the click -->\n * <lc-header\n * [showThemeButton]=\"true\"\n * [autoToggleTheme]=\"false\"\n * (themeToggleClick)=\"toggleTheme()\"\n * />\n *\n * <!-- Public site: plain links, labelled nav, language link at the end -->\n * <lc-header\n * size=\"sm\"\n * [sticky]=\"false\"\n * [showUserMenu]=\"false\"\n * homeHref=\"/de/\"\n * homeAriaLabel=\"Produkt – Startseite\"\n * navAriaLabel=\"Hauptnavigation\"\n * [navItems]=\"[\n * { label: 'Sicherheit', href: '/de/sicherheit', current: true },\n * { label: 'Roadmap', href: '/de/roadmap' }\n * ]\"\n * >\n * <a slot=\"end\" lcButton variant=\"ghost\" href=\"/en/security\" hreflang=\"en\" lang=\"en\">English</a>\n * </lc-header>\n * ```\n */\n@Component({\n selector: 'lc-header',\n standalone: true,\n imports: [\n NgTemplateOutlet,\n RouterModule,\n ButtonComponent,\n LogoComponent,\n IconComponent,\n AvatarComponent,\n MenuComponent,\n ],\n // Its own glyphs, and the profile menu's (lc-menu renders them in this view).\n viewProviders: [ɵprovideKitIcons([ICON_BARS_3, ICON_SUN, ICON_MOON, ICON_USER, ICON_CHEVRON_DOWN, ICON_LOGOUT])],\n templateUrl: './header.component.html',\n styleUrl: './header.component.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'lc-header-host',\n },\n})\nexport class HeaderComponent {\n readonly theme = input<'light' | 'dark' | 'auto'>('auto');\n readonly logo = input('');\n /**\n * Render the brand block. On its own this shows the emblem; with `logo` set\n * it shows the full lockup. (It used to additionally require `logo` or\n * `title`, so `showLogo` alone rendered an empty 0px-wide brand area with no\n * error — an app could end up with no branding at all and no clue why.)\n */\n readonly showLogo = input(true);\n /** Custom URL for the full brand logo (forwarded to inner `<lc-logo>`). */\n readonly logoSrc = input('');\n /** Custom URL for the emblem-only brand logo. */\n readonly logoEmblemSrc = input('');\n /** Optional dark-theme URL for the full brand logo. */\n readonly logoDarkSrc = input('');\n /** Optional dark-theme URL for the emblem-only brand logo. */\n readonly logoDarkEmblemSrc = input('');\n /** Alt text for the brand logo (forwarded to inner `<lc-logo>`). */\n readonly logoAlt = input('');\n /** Size of the brand logo (forwarded to inner `<lc-logo>`). Defaults to `md`. */\n readonly logoSize = input<'xs' | 'sm' | 'md' | 'lg' | 'xl'>('md');\n /**\n * Overall header height. Useful to align the header with a sidebar brand block.\n * Maps to the same `min-height` scale the sidenav logo area uses:\n * `sm` 56px · `md` 64px (default) · `lg` 80px · `xl` 112px.\n */\n readonly size = input<'sm' | 'md' | 'lg' | 'xl'>('md');\n readonly title = input('');\n /**\n * Surface: `default` (the bar on `--color-surface` with a bottom border) or\n * `transparent` (no background, no border — the header sits on the page).\n */\n readonly variant = input<HeaderVariant>('default');\n /**\n * Element for the title. `h1` keeps the app-shell behaviour; on a site whose\n * pages carry their own `<h1>`, use `span` (or `p`/`div`).\n */\n readonly titleTag = input<HeaderTitleTag>('h1');\n /**\n * Second line under the title (e.g. the household name). One line, cut\n * with \"…\" when it does not fit; the full text is its `title` attribute.\n * Shown as typed.\n */\n readonly subtitle = input('');\n readonly userEmail = input('');\n readonly userName = input('');\n /**\n * What the account-menu button shows: `icon` (user icon and chevron) or\n * `avatar` (the initials of `userName` in a 48 px round button; the button\n * is named `userMenuAriaLabel`, a comma and the initials).\n * @default 'icon'\n */\n readonly userMenuTrigger = input<HeaderUserMenuTrigger>('icon');\n /** Photo for the account avatar (trigger and menu header); initials when empty or broken. */\n readonly userAvatarSrc = input('');\n /** Colour of the account avatar's initials: `default` or a member colour `member-1` … `member-6`. */\n readonly userAvatarColor = input<AvatarColor>('default');\n /**\n * Passed to the trigger avatar and the menu avatar as `fallback` (3.15):\n * `person` shows a person symbol on a neutral disc instead of \"?\" while\n * `userName` is empty.\n * @default 'initials'\n */\n readonly userAvatarFallback = input<AvatarFallback>('initials');\n readonly showHamburger = input(false);\n /**\n * Open state of the sidenav the hamburger controls. When bound, the toggle\n * announces it via `aria-expanded`; leave `undefined` when the header does\n * not know (the attribute is then omitted rather than lying).\n */\n readonly sidenavOpen = input<boolean | undefined>(undefined);\n /** DOM id of the sidenav the hamburger controls (`aria-controls`). */\n readonly sidenavId = input('');\n readonly showThemeButton = input(false);\n /**\n * Whether the theme button toggles the `ThemeService` itself before emitting\n * `themeToggleClick`. Set to `false` when the app owns the theme switch and\n * toggles in the `(themeToggleClick)` handler — otherwise both would toggle\n * and the click would be a no-op.\n */\n readonly autoToggleTheme = input(true);\n readonly contextName = input('');\n readonly contextLabel = input('');\n /**\n * Where the context button sits up to 640 px (3.33, app-shell row 1).\n * `'inline'`: in the end group, hidden up to 640 px (as before). `'below'`:\n * up to 640 px a row of its own under the title, as wide as the header and\n * at least 44 px high; title, `end` slot and account menu stay in the first\n * row. From 641 px both render the button inline. No second row without\n * `contextName`.\n * @default 'inline'\n */\n readonly contextPlacement = input<HeaderContextPlacement>('inline');\n readonly menuSize = input<'sm' | 'md' | 'lg'>('sm');\n readonly showProfileMenuItem = input(true);\n /** Render the account menu (avatar trigger with Profile/Logout). */\n readonly showUserMenu = input(true);\n /**\n * Plain URL for the brand link. When set, the brand is a native\n * `<a href>` — no router needed (static or prerendered sites). Empty keeps\n * `routerLink=\"/\"`.\n */\n readonly homeHref = input('');\n /**\n * Main navigation links, rendered as `<nav><ul><li><a href>` with\n * `aria-current=\"page\"` on the current one. On screens below 1024 px the\n * navigation takes a second row and wraps.\n */\n readonly navItems = input<readonly HeaderNavItem[]>([]);\n /** Keep the header at the top while scrolling (`position: sticky`). */\n readonly sticky = input(true);\n\n // Labels (defaults are English; translate them in the app).\n /** Accessible name of the brand link. */\n readonly homeAriaLabel = input('Go to home');\n /** Accessible name of the hamburger button. */\n readonly hamburgerAriaLabel = input('Toggle sidebar menu');\n /**\n * Accessible name of the account menu trigger. With `userMenuTrigger=\"avatar\"`\n * the button is named this text, a comma and the initials it shows\n * (`Menü für Johanna Müller, JM`), so a voice-control user can say what they\n * see (WCAG 2.5.3). Nothing is added when `userName` gives no initials, or\n * when the text already contains them as a word. The name is the same with a\n * photo.\n * @default 'Open user menu'\n */\n readonly userMenuAriaLabel = input('Open user menu');\n /** Accessible name of the `<nav>` around `navItems`. */\n readonly navAriaLabel = input('Main navigation');\n /** Label of the theme button while the light theme is active. */\n readonly themeToDarkAriaLabel = input('Switch to dark mode');\n /** Label of the theme button while the dark theme is active. */\n readonly themeToLightAriaLabel = input('Switch to light mode');\n /**\n * Start of the context button's accessible name. The visible `contextLabel`\n * and `contextName` follow it (WCAG 2.5.3 Label in Name), unless the label\n * already contains them: `Context info, Tenant Acme Corp`.\n * @default 'Context info'\n */\n readonly contextAriaLabel = input('Context info');\n /** Label of the Profile entry in the account menu. */\n readonly profileLabel = input('Profile');\n /** Label of the Logout entry in the account menu. */\n readonly logoutLabel = input('Logout');\n\n readonly hamburgerClick = output<void>();\n readonly themeToggleClick = output<void>();\n readonly logoutClick = output<void>();\n readonly profileClick = output<void>();\n readonly contextClick = output<void>();\n\n private readonly injector = inject(Injector);\n private themeServiceInstance: ThemeService | null = null;\n\n /**\n * Resolved on first use, i.e. only when the theme button is shown: creating\n * the ThemeService reads and writes the theme class and localStorage, which a\n * site without a theme switch must not do.\n */\n protected get themeService(): ThemeService {\n return (this.themeServiceInstance ??= this.injector.get(ThemeService));\n }\n\n private readonly hamburger = viewChild('hamburger', { read: ElementRef<HTMLElement> });\n\n /** The context button (`contextName`), while it is rendered. */\n private readonly contextButton = viewChild<ElementRef<HTMLButtonElement>>('contextButton');\n\n /** The `focusContext()` hook waiting for the next render. */\n private pendingContextFocus?: AfterRenderRef;\n\n constructor() {\n // The hamburger is an <lc-button>; the ARIA state belongs on its native\n // <button> (the element that has the button role), not on the custom\n // element host — so it is reflected after render rather than bound in the\n // template.\n afterRenderEffect(() => {\n const button = this.hamburger()?.nativeElement.querySelector('button');\n if (!button) return;\n const open = this.sidenavOpen();\n const controls = this.sidenavId();\n if (open === undefined) {\n button.removeAttribute('aria-expanded');\n } else {\n button.setAttribute('aria-expanded', String(open));\n }\n if (controls) {\n button.setAttribute('aria-controls', controls);\n } else {\n button.removeAttribute('aria-controls');\n }\n });\n }\n\n /**\n * Name of the avatar trigger (AS-33, WCAG 2.5.3 Label in Name):\n * `userMenuAriaLabel`, a comma and the initials the avatar shows, so the\n * visible text is a word of the name. Built from the same function as the\n * avatar's initials and independent of the photo, so the prerendered name\n * and the page's agree. Nothing is added for the `?` placeholder or when\n * the label already holds the initials as a word (parentheticals dropped,\n * as axe-core 4.14 does); an empty label becomes the initials alone.\n */\n protected readonly avatarTriggerAriaLabel = computed(() => {\n const initials = ɵavatarInitials(this.userName());\n const label = this.userMenuAriaLabel();\n if (!/[\\p{L}\\p{N}]/u.test(initials)) return label;\n // toLowerCase, not toLocaleLowerCase: server and browser must agree.\n const words = label\n .replace(/\\([^()]*\\)/g, ' ')\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u);\n if (words.includes(initials.toLowerCase())) return label;\n if (label.trim() === '') return initials;\n return `${label}, ${initials}`;\n });\n\n /**\n * Name of the context button (AS-33, WCAG 2.5.3 Label in Name):\n * `contextAriaLabel`, a comma and the visible `contextLabel` and\n * `contextName`, so a voice-control user can say what they see.\n */\n protected readonly contextButtonAriaLabel = computed(() =>\n nameWithText(this.contextAriaLabel(), [this.contextLabel(), this.contextName()].filter(Boolean).join(' ')),\n );\n\n /** `contextPlacement=\"below\"` with a context button to place (3.33). */\n protected readonly contextBelow = computed(() => this.contextPlacement() === 'below' && !!this.contextName());\n\n /**\n * Get menu items for profile dropdown\n */\n protected readonly menuItems = computed<MenuItem[]>(() => {\n const items: MenuItem[] = [];\n\n // Add profile link if enabled\n if (this.showProfileMenuItem()) {\n items.push({\n id: 'profile',\n label: this.profileLabel(),\n icon: 'user',\n dividerAfter: true,\n });\n }\n\n // Add logout\n items.push({\n id: 'logout',\n label: this.logoutLabel(),\n icon: 'arrow-right-start-on-rectangle',\n variant: 'danger',\n });\n\n return items;\n });\n\n /**\n * Dropdown open state (using Angular signals)\n */\n private readonly dropdownOpen = signal<boolean>(false);\n\n /**\n * Get dropdown open state\n */\n isDropdownOpen(): boolean {\n return this.dropdownOpen();\n }\n\n /**\n * Toggle profile dropdown visibility\n */\n toggleDropdown(): void {\n this.dropdownOpen.set(!this.dropdownOpen());\n }\n\n /**\n * Close profile dropdown\n */\n closeDropdown(): void {\n this.dropdownOpen.set(false);\n }\n\n /**\n * Moves the focus to the context button after the next render (3.34), so a\n * `contextName` set just before is already in its name. Call it right after\n * the choice, or when the sheet that changes the context closes. Does\n * nothing while the button isn't shown (no `contextName`, or up to 640 px\n * with `contextPlacement=\"inline\"`). A second call before that render\n * replaces the first.\n */\n focusContext(): void {\n this.pendingContextFocus?.destroy();\n this.pendingContextFocus = afterNextRender(\n () => {\n this.pendingContextFocus = undefined;\n this.contextButton()?.nativeElement.focus();\n },\n { injector: this.injector },\n );\n }\n\n /**\n * Handle hamburger click\n */\n onHamburgerClick(): void {\n this.hamburgerClick.emit();\n }\n\n /**\n * Handle theme button click (separate button, not in menu). Toggles the\n * theme unless the app opted out via `autoToggleTheme`, then emits.\n */\n onThemeButtonClick(): void {\n if (this.autoToggleTheme()) {\n this.themeService.toggleTheme();\n }\n this.themeToggleClick.emit();\n }\n\n /**\n * Handle logout click\n */\n onLogoutClick(): void {\n this.logoutClick.emit();\n this.closeDropdown();\n }\n\n /**\n * Handle profile click\n */\n onProfileClick(): void {\n this.profileClick.emit();\n this.closeDropdown();\n }\n\n /**\n * Handle menu item click\n */\n onMenuItemClick(item: MenuItem): void {\n if (item.id === 'logout') {\n this.onLogoutClick();\n } else if (item.id === 'profile') {\n this.onProfileClick();\n }\n }\n}\n","<header\n class=\"lc-header\"\n [class.lc-header--dark]=\"theme() === 'dark'\"\n [class.lc-header--light]=\"theme() === 'light'\"\n [class.lc-header--size-sm]=\"size() === 'sm'\"\n [class.lc-header--size-md]=\"size() === 'md'\"\n [class.lc-header--size-lg]=\"size() === 'lg'\"\n [class.lc-header--size-xl]=\"size() === 'xl'\"\n [class.lc-header--has-nav]=\"navItems().length > 0\"\n [class.lc-header--static]=\"!sticky()\"\n [class.lc-header--transparent]=\"variant() === 'transparent'\"\n [class.lc-header--context-below]=\"contextBelow()\"\n>\n <!-- Hamburger menu icon (mobile only) -->\n @if (showHamburger()) {\n <lc-button\n #hamburger\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"hamburgerAriaLabel()\"\n (clicked)=\"onHamburgerClick()\"\n class=\"lc-header__hamburger\"\n >\n <lc-icon name=\"bars-3\" size=\"sm\" [decorative]=\"true\" />\n </lc-button>\n }\n\n <!-- Logo (clickable, navigates to home) -->\n <div class=\"lc-header__brand\">\n @if (showLogo()) {\n @if (homeHref()) {\n <!-- Plain link: works without the router and without JavaScript. -->\n <a [href]=\"homeHref()\" class=\"lc-header__logo\" [attr.aria-label]=\"homeAriaLabel()\">\n <ng-container [ngTemplateOutlet]=\"brandLogo\" />\n </a>\n } @else {\n <a routerLink=\"/\" class=\"lc-header__logo\" [attr.aria-label]=\"homeAriaLabel()\">\n <ng-container [ngTemplateOutlet]=\"brandLogo\" />\n </a>\n }\n }\n\n @if (title()) {\n <div class=\"lc-header__title-group\">\n @switch (titleTag()) {\n @case ('h1') {\n <h1 class=\"lc-header__title\">{{ title() }}</h1>\n }\n @case ('h2') {\n <h2 class=\"lc-header__title\">{{ title() }}</h2>\n }\n @case ('h3') {\n <h3 class=\"lc-header__title\">{{ title() }}</h3>\n }\n @case ('p') {\n <p class=\"lc-header__title\">{{ title() }}</p>\n }\n @case ('div') {\n <div class=\"lc-header__title\">{{ title() }}</div>\n }\n @default {\n <span class=\"lc-header__title\">{{ title() }}</span>\n }\n }\n @if (subtitle()) {\n <span class=\"lc-header__subtitle\" [attr.title]=\"subtitle()\">{{ subtitle() }}</span>\n }\n </div>\n }\n </div>\n\n <!-- Main navigation: inline from 1024 px, its own wrapping row below. -->\n @if (navItems().length > 0) {\n <nav class=\"lc-header__nav\" [attr.aria-label]=\"navAriaLabel()\">\n <ul class=\"lc-header__nav-list\">\n @for (item of navItems(); track $index) {\n <li class=\"lc-header__nav-item\">\n <a\n class=\"lc-header__nav-link\"\n [href]=\"item.href\"\n [attr.aria-current]=\"item.current ? 'page' : null\"\n >{{ item.label }}</a>\n </li>\n }\n </ul>\n </nav>\n }\n\n <!-- Zero-width spacer on the brand's row: keeps 3.10's two gaps before the end\n group and the first row's height; grows in a has-nav header from 1024 px. -->\n <div class=\"lc-header__spacer\"></div>\n\n <!-- End of the bar: the end slot (e.g. a language link), theme toggle, context\n info and account menu as one group. It goes to the end of a second row\n when it doesn't fit beside the brand (AS-31). No role; the DOM order and\n so the focus order stay brand → nav → end. -->\n <div class=\"lc-header__end\">\n <ng-content select=\"[slot='end']\" />\n\n <!-- Theme toggle button -->\n @if (showThemeButton()) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"themeService.isDark() ? themeToLightAriaLabel() : themeToDarkAriaLabel()\"\n (clicked)=\"onThemeButtonClick()\"\n class=\"lc-header__theme-toggle\"\n >\n <lc-icon [name]=\"themeService.isDark() ? 'sun' : 'moon'\" size=\"sm\" [decorative]=\"true\" />\n </lc-button>\n }\n\n <!-- Context info (e.g. tenant, organization, project) -->\n @if (contextName()) {\n <button\n #contextButton\n type=\"button\"\n class=\"lc-header__context-info\"\n [attr.aria-label]=\"contextButtonAriaLabel()\"\n (click)=\"contextClick.emit()\"\n >\n <!-- A real space between label and name: the visible text reads\n \"Tenant Acme Corp\", not \"TenantAcme Corp\" (WCAG 2.5.3). The\n tooltip sits on the name, so it is not a description of the button. -->\n @if (contextLabel()) {\n <span class=\"lc-header__context-label\">{{ contextLabel() }}</span>&ngsp;\n }\n <span class=\"lc-header__context-name\" [attr.title]=\"contextName()\">{{ contextName() }}</span>\n </button>\n }\n\n <!-- User profile menu -->\n @if (showUserMenu()) {\n <lc-menu\n [items]=\"menuItems()\"\n [isOpen]=\"isDropdownOpen()\"\n position=\"bottom-right\"\n minWidth=\"min(220px, calc(100vw - 2rem))\"\n maxWidth=\"min(18rem, calc(100vw - 2rem))\"\n [size]=\"menuSize()\"\n (itemClick)=\"onMenuItemClick($event)\"\n (closed)=\"closeDropdown()\"\n >\n <!-- Menu header with avatar, name and email -->\n @if (userName() || userEmail()) {\n <div header class=\"lc-header__menu-header\">\n <lc-avatar\n [name]=\"userName()\"\n [src]=\"userAvatarSrc() || undefined\"\n [color]=\"userAvatarColor()\"\n [fallback]=\"userAvatarFallback()\"\n size=\"md\"\n />\n <div class=\"lc-header__menu-user-info\">\n @if (userName()) {\n <div class=\"lc-header__menu-user-name\">{{ userName() }}</div>\n }\n @if (userEmail()) {\n <div class=\"lc-header__menu-user-email\">{{ userEmail() }}</div>\n }\n </div>\n </div>\n }\n\n @if (userMenuTrigger() === 'avatar') {\n <button\n trigger\n class=\"lc-header__profile-trigger lc-header__profile-trigger--avatar\"\n type=\"button\"\n [attr.aria-label]=\"avatarTriggerAriaLabel()\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-avatar\n [name]=\"userName()\"\n [src]=\"userAvatarSrc() || undefined\"\n [color]=\"userAvatarColor()\"\n [fallback]=\"userAvatarFallback()\"\n [decorative]=\"true\"\n size=\"md\"\n />\n </button>\n } @else {\n <button\n trigger\n class=\"lc-header__profile-trigger\"\n type=\"button\"\n [attr.aria-label]=\"userMenuAriaLabel()\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-icon name=\"user\" size=\"sm\" [decorative]=\"true\" />\n <lc-icon name=\"chevron-down\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </lc-menu>\n }\n </div>\n</header>\n\n<ng-template #brandLogo>\n <lc-logo\n [variant]=\"logo() ? 'full' : 'emblem'\"\n [size]=\"logoSize()\"\n [clickable]=\"false\"\n [colorMode]=\"theme() === 'dark' ? 'dark' : theme() === 'light' ? 'light' : 'auto'\"\n [src]=\"logoSrc()\"\n [emblemSrc]=\"logoEmblemSrc()\"\n [darkSrc]=\"logoDarkSrc()\"\n [darkEmblemSrc]=\"logoDarkEmblemSrc()\"\n [alt]=\"logoAlt() || title() || 'Logo'\"\n />\n</ng-template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-header.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/header/header.component.ts","../../../../libs/angular-ui-kit/src/lib/header/header.component.html"],"sourcesContent":["import {\n AfterRenderRef,\n Component,\n ElementRef,\n Injector,\n afterNextRender,\n input,\n output,\n signal,\n computed,\n inject,\n viewChild,\n afterRenderEffect,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterModule } from '@angular/router';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { LogoComponent } from '@lifecorner/angular-ui-kit/logo';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_BARS_3, ICON_CHEVRON_DOWN, ICON_LOGOUT, ICON_MOON, ICON_SUN, ICON_USER } from '@lifecorner/angular-ui-kit/icons';\nimport { AvatarColor, AvatarComponent, AvatarFallback, ɵavatarInitials } from '@lifecorner/angular-ui-kit/avatar';\nimport { MenuComponent, MenuItem } from '@lifecorner/angular-ui-kit/menu';\nimport { ThemeService } from '@lifecorner/angular-ui-kit/theme';\nimport { BadgeComponent, type BadgeVariant } from '@lifecorner/angular-ui-kit/badge';\nimport { containsWords, labelWords, nameWithText } from '@lifecorner/angular-ui-kit/internal/a11y';\n\n/** A link in the header's main navigation (`navItems`). */\nexport interface HeaderNavItem {\n /** Visible link text. */\n label: string;\n /** Plain URL — rendered as a native `href`, no router involved. */\n href: string;\n /** Marks the page the visitor is on (`aria-current=\"page\"` + underline). */\n current?: boolean;\n}\n\n/** Element used for the header title (`titleTag`). */\nexport type HeaderTitleTag = 'h1' | 'h2' | 'h3' | 'p' | 'span' | 'div';\n\n/**\n * Surface of the header: `default` is the bar on `--color-surface` with a\n * bottom border; `transparent` has no background and no border, for a header\n * that sits on the page (\"Weite\").\n */\nexport type HeaderVariant = 'default' | 'transparent';\n\n/**\n * What the account-menu button shows: `icon` (a user icon and a chevron) or\n * `avatar` (the person's initials avatar, `userName`, `userAvatarSrc`,\n * `userAvatarColor`, in a 48 px round button).\n */\nexport type HeaderUserMenuTrigger = 'icon' | 'avatar';\n\n/**\n * Where the context button sits up to 640 px (3.33): `inline` in the end\n * group, hidden up to 640 px (as before), or `below`, a row of its own under\n * the title. From 641 px both render it inline.\n */\nexport type HeaderContextPlacement = 'inline' | 'below';\n\n/**\n * State of the header's context (3.36, `contextBadge`): an `lc-badge`\n * (`sm`, rounded) inside the context button after `contextName`, e.g.\n * `{ label: 'Freigegeben', variant: 'success' }`. Keep the label to one or two\n * words.\n */\nexport interface HeaderContextBadge {\n /** Visible badge text; it ends the button's accessible name. */\n label: string;\n /** Badge variant, as `lc-badge`. @default 'default' */\n variant?: BadgeVariant;\n}\n\n/**\n * AppHeaderComponent - Global application header for the LifeCorner shell\n *\n * Features:\n * - Clickable logo for home navigation\n * - Optional title and subtitle display\n * - User profile dropdown with avatar, name, email, optional Profile link, and Logout\n * - Optional theme toggle button in header. By default the button toggles the\n * `ThemeService` itself and additionally emits `themeToggleClick`, so the\n * handler must NOT toggle again (that would undo the switch). To own the\n * toggling in the app, set `[autoToggleTheme]=\"false\"` and toggle in the\n * handler.\n * - Hamburger menu toggle for mobile sidebar; bind `sidenavOpen` (and\n * `sidenavId`) so the toggle exposes `aria-expanded` / `aria-controls`.\n * - OnPush change detection for performance\n *\n * Public, prerendered sites (no router, no JavaScript needed):\n * - `homeHref` makes the brand a plain link (instead of `routerLink=\"/\"`).\n * - `navItems` render a labelled main navigation (`<nav aria-label><ul>` with\n * `aria-current=\"page\"` on the current item); below 1024 px it moves to a\n * second row and wraps.\n * - `[slot='end']` takes content at the end of the first row (e.g. a language\n * link), `showUserMenu` hides the account menu, `titleTag` picks the title\n * element (the page keeps its own `<h1>`), `sticky` can be switched off.\n * - The end content (slot, theme button, context, account menu) is one group:\n * when it doesn't fit beside the logo and the title's longest word (200 %\n * text on a phone), it takes the end of a second row instead of covering the\n * brand or widening the page (3.10.1, AS-31). The subtitle ends with \"…\".\n * App shell (\"Weite\", kit 3.3):\n * - `variant=\"transparent\"`: no background and no border, the header sits on\n * the page (pair it with `[sticky]=\"false\"`, a header without a surface\n * cannot stay over scrolling text).\n * - `userMenuTrigger=\"avatar\"`: the account button is the person's initials\n * avatar (`userAvatarColor` for the member colour, `userAvatarSrc` for a\n * photo, `userAvatarFallback=\"person\"` for a person symbol while there is\n * no name) in a 48 px round button; the icon trigger is 44 px high in every\n * density.\n * - The subtitle (e.g. a household name) stays on one line and ends with \"…\";\n * the full text is its `title` and stays in the accessible text. It is shown\n * as typed (no uppercase).\n * - The account menu is at most `min(18rem, 100vw − 2rem)` wide; the name and\n * email in its header wrap instead of being cut.\n * - `contextPlacement=\"below\"` (3.33): up to 640 px the context button\n * (`contextName`) takes a full-width row of its own under the title instead\n * of being hidden; from 641 px it sits in the end group as before.\n * - `focusContext()` (3.34): moves the focus to the context button after the\n * next render, e.g. after a choice in the sheet it opened (WCAG 2.4.3).\n * - `contextBadge` (3.36): the context's state as a badge inside the context\n * button, part of its accessible name; `hamburgerClick` emits the\n * hamburger's `<button>`, e.g. for `lc-sidenav [returnFocusTo]`.\n * - Every built-in label is an input (`homeAriaLabel`, `hamburgerAriaLabel`,\n * `userMenuAriaLabel`, `navAriaLabel`, `profileLabel`, `logoutLabel`,\n * `themeToDarkAriaLabel`, `themeToLightAriaLabel`, `contextAriaLabel`);\n * the defaults stay English.\n * - The `ThemeService` (which persists to localStorage) is only created when\n * the theme button is shown.\n *\n * @example\n * ```html\n * <lc-header\n * [logo]=\"'/assets/logo.svg'\"\n * [title]=\"'LifeCorner'\"\n * [subtitle]=\"'User Profile'\"\n * [userName]=\"'John Doe'\"\n * [userEmail]=\"'user@example.com'\"\n * [showHamburger]=\"true\"\n * [sidenavOpen]=\"sidenavOpen\"\n * sidenavId=\"app-sidenav\"\n * [showThemeButton]=\"true\"\n * [showProfileMenuItem]=\"true\"\n * (hamburgerClick)=\"toggleSidebar()\"\n * (themeToggleClick)=\"trackThemeToggle()\"\n * (profileClick)=\"navigateToProfile()\"\n * (logoutClick)=\"handleLogout()\"\n * />\n *\n * <!-- App-controlled theme: the header only reports the click -->\n * <lc-header\n * [showThemeButton]=\"true\"\n * [autoToggleTheme]=\"false\"\n * (themeToggleClick)=\"toggleTheme()\"\n * />\n *\n * <!-- Public site: plain links, labelled nav, language link at the end -->\n * <lc-header\n * size=\"sm\"\n * [sticky]=\"false\"\n * [showUserMenu]=\"false\"\n * homeHref=\"/de/\"\n * homeAriaLabel=\"Produkt – Startseite\"\n * navAriaLabel=\"Hauptnavigation\"\n * [navItems]=\"[\n * { label: 'Sicherheit', href: '/de/sicherheit', current: true },\n * { label: 'Roadmap', href: '/de/roadmap' }\n * ]\"\n * >\n * <a slot=\"end\" lcButton variant=\"ghost\" href=\"/en/security\" hreflang=\"en\" lang=\"en\">English</a>\n * </lc-header>\n * ```\n */\n@Component({\n selector: 'lc-header',\n standalone: true,\n imports: [\n NgTemplateOutlet,\n RouterModule,\n ButtonComponent,\n LogoComponent,\n IconComponent,\n AvatarComponent,\n MenuComponent,\n BadgeComponent,\n ],\n // Its own glyphs, and the profile menu's (lc-menu renders them in this view).\n viewProviders: [ɵprovideKitIcons([ICON_BARS_3, ICON_SUN, ICON_MOON, ICON_USER, ICON_CHEVRON_DOWN, ICON_LOGOUT])],\n templateUrl: './header.component.html',\n styleUrl: './header.component.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'lc-header-host',\n },\n})\nexport class HeaderComponent {\n readonly theme = input<'light' | 'dark' | 'auto'>('auto');\n readonly logo = input('');\n /**\n * Render the brand block. On its own this shows the emblem; with `logo` set\n * it shows the full lockup. (It used to additionally require `logo` or\n * `title`, so `showLogo` alone rendered an empty 0px-wide brand area with no\n * error — an app could end up with no branding at all and no clue why.)\n */\n readonly showLogo = input(true);\n /** Custom URL for the full brand logo (forwarded to inner `<lc-logo>`). */\n readonly logoSrc = input('');\n /** Custom URL for the emblem-only brand logo. */\n readonly logoEmblemSrc = input('');\n /** Optional dark-theme URL for the full brand logo. */\n readonly logoDarkSrc = input('');\n /** Optional dark-theme URL for the emblem-only brand logo. */\n readonly logoDarkEmblemSrc = input('');\n /** Alt text for the brand logo (forwarded to inner `<lc-logo>`). */\n readonly logoAlt = input('');\n /** Size of the brand logo (forwarded to inner `<lc-logo>`). Defaults to `md`. */\n readonly logoSize = input<'xs' | 'sm' | 'md' | 'lg' | 'xl'>('md');\n /**\n * Overall header height. Useful to align the header with a sidebar brand block.\n * Maps to the same `min-height` scale the sidenav logo area uses:\n * `sm` 56px · `md` 64px (default) · `lg` 80px · `xl` 112px.\n */\n readonly size = input<'sm' | 'md' | 'lg' | 'xl'>('md');\n readonly title = input('');\n /**\n * Surface: `default` (the bar on `--color-surface` with a bottom border) or\n * `transparent` (no background, no border — the header sits on the page).\n */\n readonly variant = input<HeaderVariant>('default');\n /**\n * Element for the title. `h1` keeps the app-shell behaviour; on a site whose\n * pages carry their own `<h1>`, use `span` (or `p`/`div`).\n */\n readonly titleTag = input<HeaderTitleTag>('h1');\n /**\n * Second line under the title (e.g. the household name). One line, cut\n * with \"…\" when it does not fit; the full text is its `title` attribute.\n * Shown as typed.\n */\n readonly subtitle = input('');\n readonly userEmail = input('');\n readonly userName = input('');\n /**\n * What the account-menu button shows: `icon` (user icon and chevron) or\n * `avatar` (the initials of `userName` in a 48 px round button; the button\n * is named `userMenuAriaLabel`, a comma and the initials).\n * @default 'icon'\n */\n readonly userMenuTrigger = input<HeaderUserMenuTrigger>('icon');\n /** Photo for the account avatar (trigger and menu header); initials when empty or broken. */\n readonly userAvatarSrc = input('');\n /** Colour of the account avatar's initials: `default` or a member colour `member-1` … `member-6`. */\n readonly userAvatarColor = input<AvatarColor>('default');\n /**\n * Passed to the trigger avatar and the menu avatar as `fallback` (3.15):\n * `person` shows a person symbol on a neutral disc instead of \"?\" while\n * `userName` is empty.\n * @default 'initials'\n */\n readonly userAvatarFallback = input<AvatarFallback>('initials');\n readonly showHamburger = input(false);\n /**\n * Open state of the sidenav the hamburger controls. When bound, the toggle\n * announces it via `aria-expanded`; leave `undefined` when the header does\n * not know (the attribute is then omitted rather than lying).\n */\n readonly sidenavOpen = input<boolean | undefined>(undefined);\n /** DOM id of the sidenav the hamburger controls (`aria-controls`). */\n readonly sidenavId = input('');\n readonly showThemeButton = input(false);\n /**\n * Whether the theme button toggles the `ThemeService` itself before emitting\n * `themeToggleClick`. Set to `false` when the app owns the theme switch and\n * toggles in the `(themeToggleClick)` handler — otherwise both would toggle\n * and the click would be a no-op.\n */\n readonly autoToggleTheme = input(true);\n readonly contextName = input('');\n readonly contextLabel = input('');\n /**\n * Where the context button sits up to 640 px (3.33, app-shell row 1).\n * `'inline'`: in the end group, hidden up to 640 px (as before). `'below'`:\n * up to 640 px a row of its own under the title, as wide as the header and\n * at least 44 px high; title, `end` slot and account menu stay in the first\n * row. From 641 px both render the button inline. No second row without\n * `contextName`.\n * @default 'inline'\n */\n readonly contextPlacement = input<HeaderContextPlacement>('inline');\n /**\n * State of the context (3.36, app-shell row 1): an `lc-badge` (`sm`,\n * rounded) inside the context button after `contextName`, e.g.\n * `{ label: 'Freigegeben', variant: 'success' }`, wherever the button shows\n * (`inline` and `below`, every size). Its label ends the button's accessible\n * name („Monatsstand wählen, Monatsstand Oktober 2026 · Fassung 1,\n * Freigegeben“, WCAG 2.5.3) unless `contextAriaLabel` already holds it. With\n * a badge the button may grow to 340 px (220 without); a long name ends with\n * \"…\" and the badge stays whole. Keep the label to one or two words. Target\n * size, focus ring, `contextClick` and `focusContext()` don't change.\n * @default undefined\n */\n readonly contextBadge = input<HeaderContextBadge | undefined>(undefined);\n readonly menuSize = input<'sm' | 'md' | 'lg'>('sm');\n readonly showProfileMenuItem = input(true);\n /** Render the account menu (avatar trigger with Profile/Logout). */\n readonly showUserMenu = input(true);\n /**\n * Plain URL for the brand link. When set, the brand is a native\n * `<a href>` — no router needed (static or prerendered sites). Empty keeps\n * `routerLink=\"/\"`.\n */\n readonly homeHref = input('');\n /**\n * Main navigation links, rendered as `<nav><ul><li><a href>` with\n * `aria-current=\"page\"` on the current one. On screens below 1024 px the\n * navigation takes a second row and wraps.\n */\n readonly navItems = input<readonly HeaderNavItem[]>([]);\n /** Keep the header at the top while scrolling (`position: sticky`). */\n readonly sticky = input(true);\n\n // Labels (defaults are English; translate them in the app).\n /** Accessible name of the brand link. */\n readonly homeAriaLabel = input('Go to home');\n /** Accessible name of the hamburger button. */\n readonly hamburgerAriaLabel = input('Toggle sidebar menu');\n /**\n * Accessible name of the account menu trigger. With `userMenuTrigger=\"avatar\"`\n * the button is named this text, a comma and the initials it shows\n * (`Menü für Johanna Müller, JM`), so a voice-control user can say what they\n * see (WCAG 2.5.3). Nothing is added when `userName` gives no initials, or\n * when the text already contains them as a word. The name is the same with a\n * photo.\n * @default 'Open user menu'\n */\n readonly userMenuAriaLabel = input('Open user menu');\n /** Accessible name of the `<nav>` around `navItems`. */\n readonly navAriaLabel = input('Main navigation');\n /** Label of the theme button while the light theme is active. */\n readonly themeToDarkAriaLabel = input('Switch to dark mode');\n /** Label of the theme button while the dark theme is active. */\n readonly themeToLightAriaLabel = input('Switch to light mode');\n /**\n * Start of the context button's accessible name. The visible `contextLabel`\n * and `contextName` follow it (WCAG 2.5.3 Label in Name), unless the label\n * already contains them: `Context info, Tenant Acme Corp`.\n * @default 'Context info'\n */\n readonly contextAriaLabel = input('Context info');\n /** Label of the Profile entry in the account menu. */\n readonly profileLabel = input('Profile');\n /** Label of the Logout entry in the account menu. */\n readonly logoutLabel = input('Logout');\n\n /**\n * Emitted when the hamburger is clicked. Since 3.36 it carries the\n * hamburger's native `<button>`, the element that has the focus, so a page\n * can hand it to `lc-sidenav`: `(hamburgerClick)=\"trigger = $event; open = true\"`\n * with `[returnFocusTo]=\"trigger\"`. Handlers without `$event` work as before.\n */\n readonly hamburgerClick = output<HTMLElement>();\n readonly themeToggleClick = output<void>();\n readonly logoutClick = output<void>();\n readonly profileClick = output<void>();\n readonly contextClick = output<void>();\n\n private readonly injector = inject(Injector);\n private readonly hostElement = inject<ElementRef<HTMLElement>>(ElementRef);\n private themeServiceInstance: ThemeService | null = null;\n\n /**\n * Resolved on first use, i.e. only when the theme button is shown: creating\n * the ThemeService reads and writes the theme class and localStorage, which a\n * site without a theme switch must not do.\n */\n protected get themeService(): ThemeService {\n return (this.themeServiceInstance ??= this.injector.get(ThemeService));\n }\n\n private readonly hamburger = viewChild('hamburger', { read: ElementRef<HTMLElement> });\n\n /** The context button (`contextName`), while it is rendered. */\n private readonly contextButton = viewChild<ElementRef<HTMLButtonElement>>('contextButton');\n\n /** The `focusContext()` hook waiting for the next render. */\n private pendingContextFocus?: AfterRenderRef;\n\n constructor() {\n // The hamburger is an <lc-button>; the ARIA state belongs on its native\n // <button> (the element that has the button role), not on the custom\n // element host — so it is reflected after render rather than bound in the\n // template.\n afterRenderEffect(() => {\n const button = this.hamburger()?.nativeElement.querySelector('button');\n if (!button) return;\n const open = this.sidenavOpen();\n const controls = this.sidenavId();\n if (open === undefined) {\n button.removeAttribute('aria-expanded');\n } else {\n button.setAttribute('aria-expanded', String(open));\n }\n if (controls) {\n button.setAttribute('aria-controls', controls);\n } else {\n button.removeAttribute('aria-controls');\n }\n });\n }\n\n /**\n * Name of the avatar trigger (AS-33, WCAG 2.5.3 Label in Name):\n * `userMenuAriaLabel`, a comma and the initials the avatar shows, so the\n * visible text is a word of the name. Built from the same function as the\n * avatar's initials and independent of the photo, so the prerendered name\n * and the page's agree. Nothing is added for the `?` placeholder or when\n * the label already holds the initials as a word (parentheticals dropped,\n * as axe-core 4.14 does); an empty label becomes the initials alone.\n */\n protected readonly avatarTriggerAriaLabel = computed(() => {\n const initials = ɵavatarInitials(this.userName());\n const label = this.userMenuAriaLabel();\n if (!/[\\p{L}\\p{N}]/u.test(initials)) return label;\n // toLowerCase, not toLocaleLowerCase: server and browser must agree.\n const words = label\n .replace(/\\([^()]*\\)/g, ' ')\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u);\n if (words.includes(initials.toLowerCase())) return label;\n if (label.trim() === '') return initials;\n return `${label}, ${initials}`;\n });\n\n /**\n * Name of the context button (AS-33, WCAG 2.5.3 Label in Name):\n * `contextAriaLabel`, a comma and the visible `contextLabel` and\n * `contextName`, so a voice-control user can say what they see.\n */\n protected readonly contextButtonAriaLabel = computed(() => {\n const name = nameWithText(this.contextAriaLabel(), [this.contextLabel(), this.contextName()].filter(Boolean).join(' '));\n // 3.36: the badge's word ends the name, unless the name already holds it.\n const badge = this.contextBadge()?.label.replace(/\\s+/g, ' ').trim() ?? '';\n if (!badge || containsWords(labelWords(name), labelWords(badge))) return name;\n return name ? `${name}, ${badge}` : badge;\n });\n\n /** `contextPlacement=\"below\"` with a context button to place (3.33). */\n protected readonly contextBelow = computed(() => this.contextPlacement() === 'below' && !!this.contextName());\n\n /**\n * Get menu items for profile dropdown\n */\n protected readonly menuItems = computed<MenuItem[]>(() => {\n const items: MenuItem[] = [];\n\n // Add profile link if enabled\n if (this.showProfileMenuItem()) {\n items.push({\n id: 'profile',\n label: this.profileLabel(),\n icon: 'user',\n dividerAfter: true,\n });\n }\n\n // Add logout\n items.push({\n id: 'logout',\n label: this.logoutLabel(),\n icon: 'arrow-right-start-on-rectangle',\n variant: 'danger',\n });\n\n return items;\n });\n\n /**\n * Dropdown open state (using Angular signals)\n */\n private readonly dropdownOpen = signal<boolean>(false);\n\n /**\n * Get dropdown open state\n */\n isDropdownOpen(): boolean {\n return this.dropdownOpen();\n }\n\n /**\n * Toggle profile dropdown visibility\n */\n toggleDropdown(): void {\n this.dropdownOpen.set(!this.dropdownOpen());\n }\n\n /**\n * Close profile dropdown\n */\n closeDropdown(): void {\n this.dropdownOpen.set(false);\n }\n\n /**\n * Moves the focus to the context button after the next render (3.34), so a\n * `contextName` set just before is already in its name. Call it right after\n * the choice, or when the sheet that changes the context closes. Does\n * nothing while the button isn't shown (no `contextName`, or up to 640 px\n * with `contextPlacement=\"inline\"`). A second call before that render\n * replaces the first.\n */\n focusContext(): void {\n this.pendingContextFocus?.destroy();\n this.pendingContextFocus = afterNextRender(\n () => {\n this.pendingContextFocus = undefined;\n this.contextButton()?.nativeElement.focus();\n },\n { injector: this.injector },\n );\n }\n\n /**\n * Handle hamburger click: emits the hamburger's native `<button>` (3.36).\n * Called without a rendered hamburger, it emits the header's host element.\n */\n onHamburgerClick(): void {\n const host = this.hamburger()?.nativeElement;\n this.hamburgerClick.emit(host?.querySelector('button') ?? host ?? this.hostElement.nativeElement);\n }\n\n /**\n * Handle theme button click (separate button, not in menu). Toggles the\n * theme unless the app opted out via `autoToggleTheme`, then emits.\n */\n onThemeButtonClick(): void {\n if (this.autoToggleTheme()) {\n this.themeService.toggleTheme();\n }\n this.themeToggleClick.emit();\n }\n\n /**\n * Handle logout click\n */\n onLogoutClick(): void {\n this.logoutClick.emit();\n this.closeDropdown();\n }\n\n /**\n * Handle profile click\n */\n onProfileClick(): void {\n this.profileClick.emit();\n this.closeDropdown();\n }\n\n /**\n * Handle menu item click\n */\n onMenuItemClick(item: MenuItem): void {\n if (item.id === 'logout') {\n this.onLogoutClick();\n } else if (item.id === 'profile') {\n this.onProfileClick();\n }\n }\n}\n","<header\n class=\"lc-header\"\n [class.lc-header--dark]=\"theme() === 'dark'\"\n [class.lc-header--light]=\"theme() === 'light'\"\n [class.lc-header--size-sm]=\"size() === 'sm'\"\n [class.lc-header--size-md]=\"size() === 'md'\"\n [class.lc-header--size-lg]=\"size() === 'lg'\"\n [class.lc-header--size-xl]=\"size() === 'xl'\"\n [class.lc-header--has-nav]=\"navItems().length > 0\"\n [class.lc-header--static]=\"!sticky()\"\n [class.lc-header--transparent]=\"variant() === 'transparent'\"\n [class.lc-header--context-below]=\"contextBelow()\"\n>\n <!-- Hamburger menu icon (mobile only) -->\n @if (showHamburger()) {\n <lc-button\n #hamburger\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"hamburgerAriaLabel()\"\n (clicked)=\"onHamburgerClick()\"\n class=\"lc-header__hamburger\"\n >\n <lc-icon name=\"bars-3\" size=\"sm\" [decorative]=\"true\" />\n </lc-button>\n }\n\n <!-- Logo (clickable, navigates to home) -->\n <div class=\"lc-header__brand\">\n @if (showLogo()) {\n @if (homeHref()) {\n <!-- Plain link: works without the router and without JavaScript. -->\n <a [href]=\"homeHref()\" class=\"lc-header__logo\" [attr.aria-label]=\"homeAriaLabel()\">\n <ng-container [ngTemplateOutlet]=\"brandLogo\" />\n </a>\n } @else {\n <a routerLink=\"/\" class=\"lc-header__logo\" [attr.aria-label]=\"homeAriaLabel()\">\n <ng-container [ngTemplateOutlet]=\"brandLogo\" />\n </a>\n }\n }\n\n @if (title()) {\n <div class=\"lc-header__title-group\">\n @switch (titleTag()) {\n @case ('h1') {\n <h1 class=\"lc-header__title\">{{ title() }}</h1>\n }\n @case ('h2') {\n <h2 class=\"lc-header__title\">{{ title() }}</h2>\n }\n @case ('h3') {\n <h3 class=\"lc-header__title\">{{ title() }}</h3>\n }\n @case ('p') {\n <p class=\"lc-header__title\">{{ title() }}</p>\n }\n @case ('div') {\n <div class=\"lc-header__title\">{{ title() }}</div>\n }\n @default {\n <span class=\"lc-header__title\">{{ title() }}</span>\n }\n }\n @if (subtitle()) {\n <span class=\"lc-header__subtitle\" [attr.title]=\"subtitle()\">{{ subtitle() }}</span>\n }\n </div>\n }\n </div>\n\n <!-- Main navigation: inline from 1024 px, its own wrapping row below. -->\n @if (navItems().length > 0) {\n <nav class=\"lc-header__nav\" [attr.aria-label]=\"navAriaLabel()\">\n <ul class=\"lc-header__nav-list\">\n @for (item of navItems(); track $index) {\n <li class=\"lc-header__nav-item\">\n <a\n class=\"lc-header__nav-link\"\n [href]=\"item.href\"\n [attr.aria-current]=\"item.current ? 'page' : null\"\n >{{ item.label }}</a>\n </li>\n }\n </ul>\n </nav>\n }\n\n <!-- Zero-width spacer on the brand's row: keeps 3.10's two gaps before the end\n group and the first row's height; grows in a has-nav header from 1024 px. -->\n <div class=\"lc-header__spacer\"></div>\n\n <!-- End of the bar: the end slot (e.g. a language link), theme toggle, context\n info and account menu as one group. It goes to the end of a second row\n when it doesn't fit beside the brand (AS-31). No role; the DOM order and\n so the focus order stay brand → nav → end. -->\n <div class=\"lc-header__end\">\n <ng-content select=\"[slot='end']\" />\n\n <!-- Theme toggle button -->\n @if (showThemeButton()) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"themeService.isDark() ? themeToLightAriaLabel() : themeToDarkAriaLabel()\"\n (clicked)=\"onThemeButtonClick()\"\n class=\"lc-header__theme-toggle\"\n >\n <lc-icon [name]=\"themeService.isDark() ? 'sun' : 'moon'\" size=\"sm\" [decorative]=\"true\" />\n </lc-button>\n }\n\n <!-- Context info (e.g. tenant, organization, project) -->\n @if (contextName()) {\n <button\n #contextButton\n type=\"button\"\n class=\"lc-header__context-info\"\n [class.lc-header__context-info--badge]=\"!!contextBadge()\"\n [attr.aria-label]=\"contextButtonAriaLabel()\"\n (click)=\"contextClick.emit()\"\n >\n <!-- A real space between label and name: the visible text reads\n \"Tenant Acme Corp\", not \"TenantAcme Corp\" (WCAG 2.5.3). The\n tooltip sits on the name, so it is not a description of the button. -->\n @if (contextLabel()) {\n <span class=\"lc-header__context-label\">{{ contextLabel() }}</span>&ngsp;\n }\n @if (contextBadge(); as badge) {\n <!-- 3.36: name and state badge on one row; the name keeps its ellipsis. -->\n <span class=\"lc-header__context-row\">\n <span class=\"lc-header__context-name\" [attr.title]=\"contextName()\">{{ contextName() }}</span>&ngsp;\n <lc-badge class=\"lc-header__context-badge\" size=\"sm\" [rounded]=\"true\" [variant]=\"badge.variant ?? 'default'\">{{ badge.label }}</lc-badge>\n </span>\n } @else {\n <span class=\"lc-header__context-name\" [attr.title]=\"contextName()\">{{ contextName() }}</span>\n }\n </button>\n }\n\n <!-- User profile menu -->\n @if (showUserMenu()) {\n <lc-menu\n [items]=\"menuItems()\"\n [isOpen]=\"isDropdownOpen()\"\n position=\"bottom-right\"\n minWidth=\"min(220px, calc(100vw - 2rem))\"\n maxWidth=\"min(18rem, calc(100vw - 2rem))\"\n [size]=\"menuSize()\"\n (itemClick)=\"onMenuItemClick($event)\"\n (closed)=\"closeDropdown()\"\n >\n <!-- Menu header with avatar, name and email -->\n @if (userName() || userEmail()) {\n <div header class=\"lc-header__menu-header\">\n <lc-avatar\n [name]=\"userName()\"\n [src]=\"userAvatarSrc() || undefined\"\n [color]=\"userAvatarColor()\"\n [fallback]=\"userAvatarFallback()\"\n size=\"md\"\n />\n <div class=\"lc-header__menu-user-info\">\n @if (userName()) {\n <div class=\"lc-header__menu-user-name\">{{ userName() }}</div>\n }\n @if (userEmail()) {\n <div class=\"lc-header__menu-user-email\">{{ userEmail() }}</div>\n }\n </div>\n </div>\n }\n\n @if (userMenuTrigger() === 'avatar') {\n <button\n trigger\n class=\"lc-header__profile-trigger lc-header__profile-trigger--avatar\"\n type=\"button\"\n [attr.aria-label]=\"avatarTriggerAriaLabel()\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-avatar\n [name]=\"userName()\"\n [src]=\"userAvatarSrc() || undefined\"\n [color]=\"userAvatarColor()\"\n [fallback]=\"userAvatarFallback()\"\n [decorative]=\"true\"\n size=\"md\"\n />\n </button>\n } @else {\n <button\n trigger\n class=\"lc-header__profile-trigger\"\n type=\"button\"\n [attr.aria-label]=\"userMenuAriaLabel()\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-icon name=\"user\" size=\"sm\" [decorative]=\"true\" />\n <lc-icon name=\"chevron-down\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </lc-menu>\n }\n </div>\n</header>\n\n<ng-template #brandLogo>\n <lc-logo\n [variant]=\"logo() ? 'full' : 'emblem'\"\n [size]=\"logoSize()\"\n [clickable]=\"false\"\n [colorMode]=\"theme() === 'dark' ? 'dark' : theme() === 'light' ? 'light' : 'auto'\"\n [src]=\"logoSrc()\"\n [emblemSrc]=\"logoEmblemSrc()\"\n [darkSrc]=\"logoDarkSrc()\"\n [darkEmblemSrc]=\"logoDarkEmblemSrc()\"\n [alt]=\"logoAlt() || title() || 'Logo'\"\n />\n</ng-template>\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
- import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, computed, input, viewChild } from "@angular/core";
3
+ let nextHeroId = 0;
3
4
  var HeroComponent = class HeroComponent {
4
5
  label = input(void 0, ...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
5
6
  title = input.required(...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
@@ -8,6 +9,21 @@ var HeroComponent = class HeroComponent {
8
9
  borderRadius = input("lg", ...ngDevMode ? [{ debugName: "borderRadius" }] : /* istanbul ignore next */ []);
9
10
  variant = input("default", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
10
11
  contentWidth = input("full", ...ngDevMode ? [{ debugName: "contentWidth" }] : /* istanbul ignore next */ []);
12
+ headingLevel = input(1, ...ngDevMode ? [{ debugName: "headingLevel" }] : /* istanbul ignore next */ []);
13
+ titleEl = viewChild("titleEl", ...ngDevMode ? [{ debugName: "titleEl" }] : /* istanbul ignore next */ []);
14
+ labelEl = viewChild("labelEl", ...ngDevMode ? [{ debugName: "labelEl" }] : /* istanbul ignore next */ []);
15
+ focusTitle() {
16
+ const title = this.titleEl()?.nativeElement;
17
+ if (!title) return;
18
+ const label = this.labelEl()?.nativeElement;
19
+ if (label) {
20
+ title.id ||= `lc-hero-title-${++nextHeroId}`;
21
+ label.id ||= `lc-hero-label-${++nextHeroId}`;
22
+ title.setAttribute("aria-labelledby", `${label.id} ${title.id}`);
23
+ } else title.removeAttribute("aria-labelledby");
24
+ title.setAttribute("tabindex", "-1");
25
+ title.focus();
26
+ }
11
27
  heroClasses = computed(() => {
12
28
  return [
13
29
  "hero",
@@ -81,11 +97,31 @@ var HeroComponent = class HeroComponent {
81
97
  isSignal: true,
82
98
  isRequired: false,
83
99
  transformFunction: null
100
+ },
101
+ headingLevel: {
102
+ classPropertyName: "headingLevel",
103
+ publicName: "headingLevel",
104
+ isSignal: true,
105
+ isRequired: false,
106
+ transformFunction: null
84
107
  }
85
108
  },
109
+ viewQueries: [{
110
+ propertyName: "titleEl",
111
+ first: true,
112
+ predicate: ["titleEl"],
113
+ descendants: true,
114
+ isSignal: true
115
+ }, {
116
+ propertyName: "labelEl",
117
+ first: true,
118
+ predicate: ["labelEl"],
119
+ descendants: true,
120
+ isSignal: true
121
+ }],
86
122
  ngImport: i0,
87
- template: "<div [class]=\"heroClasses()\">\n <div class=\"hero__inner\">\n <div class=\"hero__content\">\n @if (label()) {\n <span class=\"hero__label\">{{ label() }}</span>\n }\n <h1 class=\"hero__title\">{{ title() }}</h1>\n <div class=\"hero__body\">\n <ng-content></ng-content>\n </div>\n </div>\n <div class=\"hero__media\">\n <ng-content select=\"[hero-media]\"></ng-content>\n </div>\n </div>\n <ng-content select=\"[hero-footer]\"></ng-content>\n</div>\n",
88
- styles: [":host{display:block;width:100%}.hero{position:relative;display:flex;flex-direction:column;justify-content:space-between;color:#fff;overflow:hidden}.hero--primary{background:linear-gradient(135deg,#0d353d,#208497)}.hero--secondary{background:linear-gradient(135deg,#2d3c46,#7097af)}.hero--neutral{background:linear-gradient(135deg,#111827,#4b5563)}.hero--success{background:linear-gradient(135deg,#5f7150,#8ea475)}.hero--info{background:linear-gradient(135deg,#27445c,#3b6588)}.hero--warning{background:linear-gradient(135deg,#9b6b2b,#e1a040)}.hero--accent-orange{background:linear-gradient(135deg,#7a3a10,#df792e)}.hero--accent-purple{background:linear-gradient(135deg,#3a2a52,#866aa0)}.hero--accent-violet{background:linear-gradient(135deg,#221a42,#4e3f84)}.hero--sm{padding:1.5rem 1.25rem}@media(min-width:640px){.hero--sm{padding:2rem}}@media(min-width:1024px){.hero--sm{padding:2.5rem 3rem}}.hero--md{padding:2rem 1.25rem}@media(min-width:640px){.hero--md{padding:2.5rem 2rem}}@media(min-width:1024px){.hero--md{padding:3rem 3.5rem}}.hero--lg{padding:2.5rem 1.25rem}@media(min-width:640px){.hero--lg{padding:3rem 2.5rem}}@media(min-width:1024px){.hero--lg{padding:4rem}}.hero--radius-none{border-radius:var(--border-radius-none)}.hero--radius-sm{border-radius:var(--border-radius-sm)}.hero--radius-md{border-radius:var(--border-radius-md)}.hero--radius-lg{border-radius:var(--border-radius-lg)}.hero--slim{padding:1rem 1.25rem!important}@media(min-width:640px){.hero--slim{padding:1.25rem 2rem!important}}@media(min-width:1024px){.hero--slim{padding:1.5rem 3rem!important}}.hero--slim .hero__title{font-size:1.125rem!important;font-weight:700;line-height:1.3}@media(min-width:640px){.hero--slim .hero__title{font-size:1.25rem!important}}@media(min-width:1024px){.hero--slim .hero__title{font-size:1.5rem!important}}.hero--slim .hero__label{font-size:.5625rem;margin-bottom:.375rem;letter-spacing:.12em}@media(min-width:640px){.hero--slim .hero__label{font-size:.625rem;margin-bottom:.5rem}}.hero--slim .hero__body{margin-top:.5rem;font-size:.8125rem}@media(min-width:640px){.hero--slim .hero__body{font-size:.875rem;margin-top:.625rem}}.hero--slim ::ng-deep [hero-footer]{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim ::ng-deep [hero-footer]{margin-top:1.25rem}}.hero--light{color:#1f2937}.hero--light .hero__label{opacity:.7}.hero--light .hero__body{opacity:.75}.hero--light ::ng-deep [hero-footer]{border-top-color:#0000001f}.hero--light.hero--primary{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--secondary{background:linear-gradient(135deg,#e0e7ff,#eef2ff)}.hero--light.hero--neutral{background:linear-gradient(135deg,#e5e7eb,#f9fafb)}.hero--light.hero--success{background:linear-gradient(135deg,#d1fae5,#ecfdf5)}.hero--light.hero--info{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--warning{background:linear-gradient(135deg,#fef3c7,#fffbeb)}.hero--light.hero--accent-orange{background:linear-gradient(135deg,#ffedd5,#fff7ed)}.hero--light.hero--accent-purple{background:linear-gradient(135deg,#e9d5ff,#f5f3ff)}.hero--light.hero--accent-violet{background:linear-gradient(135deg,#ddd6fe,#ede9fe)}@media(min-width:1024px){.hero--content-lg.hero--sm{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--md{padding-inline:max(3.5rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--lg{padding-inline:max(4rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--slim{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)!important}}.hero__inner{flex:1}.hero__content{min-width:0}.hero__label{display:inline-block;font-size:.6875rem;font-weight:600;letter-spacing:.15em;text-transform:uppercase;opacity:.8;margin-bottom:.75rem}@media(min-width:640px){.hero__label{font-size:.75rem;margin-bottom:1rem}}.hero__title{margin:0;font-weight:800;line-height:1.1;color:inherit}.hero--sm .hero__title{font-size:1.5rem}@media(min-width:640px){.hero--sm .hero__title{font-size:1.75rem}}@media(min-width:1024px){.hero--sm .hero__title{font-size:2rem}}.hero--md .hero__title{font-size:1.75rem}@media(min-width:640px){.hero--md .hero__title{font-size:2.25rem}}@media(min-width:1024px){.hero--md .hero__title{font-size:2.75rem}}.hero--lg .hero__title{font-size:2rem}@media(min-width:640px){.hero--lg .hero__title{font-size:2.75rem}}@media(min-width:1024px){.hero--lg .hero__title{font-size:3.5rem}}.hero__body{margin-top:1rem;font-size:.875rem;line-height:1.6;opacity:.85;max-width:48rem}@media(min-width:640px){.hero__body{font-size:1rem;margin-top:1.25rem}}@media(min-width:1024px){.hero__body{font-size:1.125rem;margin-top:1.5rem}}.hero__body:empty{display:none}.hero__media{max-width:var(--lc-hero-media-width, 23rem);margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){.hero__media{margin-top:2.5rem}}.hero__media:empty{display:none}.hero--slim .hero__media{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim .hero__media{margin-top:1.25rem}}.hero--light .hero__media{border-top-color:#0000001f}@media(min-width:1024px){.hero__inner:has(>.hero__media:not(:empty)){display:grid;grid-template-columns:minmax(0,1fr) var(--lc-hero-media-width, 23rem);column-gap:3.5rem;align-items:center}.hero__inner:has(>.hero__media:not(:empty))>.hero__media{max-width:none;margin-top:0;padding-top:0;border-top:0}}:host ::ng-deep [hero-footer]{display:grid;grid-template-columns:1fr;gap:1rem;margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(2,1fr);margin-top:2.5rem}}@media(min-width:1024px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(auto-fit,minmax(10rem,1fr));margin-top:3rem}}\n"],
123
+ template: "<div [class]=\"heroClasses()\">\n <div class=\"hero__inner\">\n <div class=\"hero__content\">\n @if (label()) {\n <span #labelEl class=\"hero__label\">{{ label() }}</span>\n }\n <!-- 3.36: the element follows `headingLevel`; the look is the same at every level. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 #titleEl class=\"hero__title\">{{ title() }}</h2>\n }\n @case (3) {\n <h3 #titleEl class=\"hero__title\">{{ title() }}</h3>\n }\n @case (4) {\n <h4 #titleEl class=\"hero__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 #titleEl class=\"hero__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 #titleEl class=\"hero__title\">{{ title() }}</h6>\n }\n @default {\n <h1 #titleEl class=\"hero__title\">{{ title() }}</h1>\n }\n }\n <div class=\"hero__body\">\n <ng-content></ng-content>\n </div>\n </div>\n <div class=\"hero__media\">\n <ng-content select=\"[hero-media]\"></ng-content>\n </div>\n </div>\n <ng-content select=\"[hero-footer]\"></ng-content>\n</div>\n",
124
+ styles: [":host{display:block;width:100%}.hero{position:relative;display:flex;flex-direction:column;justify-content:space-between;color:#fff;overflow:hidden}.hero--primary{background:linear-gradient(135deg,#0d353d,#208497)}.hero--secondary{background:linear-gradient(135deg,#2d3c46,#7097af)}.hero--neutral{background:linear-gradient(135deg,#111827,#4b5563)}.hero--success{background:linear-gradient(135deg,#5f7150,#8ea475)}.hero--info{background:linear-gradient(135deg,#27445c,#3b6588)}.hero--warning{background:linear-gradient(135deg,#9b6b2b,#e1a040)}.hero--accent-orange{background:linear-gradient(135deg,#7a3a10,#df792e)}.hero--accent-purple{background:linear-gradient(135deg,#3a2a52,#866aa0)}.hero--accent-violet{background:linear-gradient(135deg,#221a42,#4e3f84)}.hero--sm{padding:1.5rem 1.25rem}@media(min-width:640px){.hero--sm{padding:2rem}}@media(min-width:1024px){.hero--sm{padding:2.5rem 3rem}}.hero--md{padding:2rem 1.25rem}@media(min-width:640px){.hero--md{padding:2.5rem 2rem}}@media(min-width:1024px){.hero--md{padding:3rem 3.5rem}}.hero--lg{padding:2.5rem 1.25rem}@media(min-width:640px){.hero--lg{padding:3rem 2.5rem}}@media(min-width:1024px){.hero--lg{padding:4rem}}.hero--radius-none{border-radius:var(--border-radius-none)}.hero--radius-sm{border-radius:var(--border-radius-sm)}.hero--radius-md{border-radius:var(--border-radius-md)}.hero--radius-lg{border-radius:var(--border-radius-lg)}.hero--slim{padding:1rem 1.25rem!important}@media(min-width:640px){.hero--slim{padding:1.25rem 2rem!important}}@media(min-width:1024px){.hero--slim{padding:1.5rem 3rem!important}}.hero--slim .hero__title{font-size:1.125rem!important;font-weight:700;line-height:1.3}@media(min-width:640px){.hero--slim .hero__title{font-size:1.25rem!important}}@media(min-width:1024px){.hero--slim .hero__title{font-size:1.5rem!important}}.hero--slim .hero__label{font-size:.5625rem;margin-bottom:.375rem;letter-spacing:.12em}@media(min-width:640px){.hero--slim .hero__label{font-size:.625rem;margin-bottom:.5rem}}.hero--slim .hero__body{margin-top:.5rem;font-size:.8125rem}@media(min-width:640px){.hero--slim .hero__body{font-size:.875rem;margin-top:.625rem}}.hero--slim ::ng-deep [hero-footer]{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim ::ng-deep [hero-footer]{margin-top:1.25rem}}.hero--light{color:#1f2937}.hero--light .hero__label{opacity:.7}.hero--light .hero__body{opacity:.75}.hero--light ::ng-deep [hero-footer]{border-top-color:#0000001f}.hero--light.hero--primary{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--secondary{background:linear-gradient(135deg,#e0e7ff,#eef2ff)}.hero--light.hero--neutral{background:linear-gradient(135deg,#e5e7eb,#f9fafb)}.hero--light.hero--success{background:linear-gradient(135deg,#d1fae5,#ecfdf5)}.hero--light.hero--info{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--warning{background:linear-gradient(135deg,#fef3c7,#fffbeb)}.hero--light.hero--accent-orange{background:linear-gradient(135deg,#ffedd5,#fff7ed)}.hero--light.hero--accent-purple{background:linear-gradient(135deg,#e9d5ff,#f5f3ff)}.hero--light.hero--accent-violet{background:linear-gradient(135deg,#ddd6fe,#ede9fe)}@media(min-width:1024px){.hero--content-lg.hero--sm{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--md{padding-inline:max(3.5rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--lg{padding-inline:max(4rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--slim{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)!important}}.hero__inner{flex:1}.hero__content{min-width:0}.hero__label{display:inline-block;font-size:.6875rem;font-weight:600;letter-spacing:.15em;text-transform:uppercase;opacity:.8;margin-bottom:.75rem}@media(min-width:640px){.hero__label{font-size:.75rem;margin-bottom:1rem}}.hero__title{margin:0;font-weight:800;line-height:1.1;color:inherit}.hero--sm .hero__title{font-size:1.5rem}@media(min-width:640px){.hero--sm .hero__title{font-size:1.75rem}}@media(min-width:1024px){.hero--sm .hero__title{font-size:2rem}}.hero--md .hero__title{font-size:1.75rem}@media(min-width:640px){.hero--md .hero__title{font-size:2.25rem}}@media(min-width:1024px){.hero--md .hero__title{font-size:2.75rem}}.hero--lg .hero__title{font-size:2rem}@media(min-width:640px){.hero--lg .hero__title{font-size:2.75rem}}@media(min-width:1024px){.hero--lg .hero__title{font-size:3.5rem}}.hero__title:focus-visible{outline:2px solid currentColor;outline-offset:2px;box-shadow:0 0 0 2px #111827;border-radius:var(--border-radius-sm)}.hero--light .hero__title:focus-visible{box-shadow:0 0 0 2px #fff}@media(forced-colors:active){.hero__title:focus-visible{outline-color:Highlight;box-shadow:none}}.hero__body{margin-top:1rem;font-size:.875rem;line-height:1.6;opacity:.85;max-width:48rem}@media(min-width:640px){.hero__body{font-size:1rem;margin-top:1.25rem}}@media(min-width:1024px){.hero__body{font-size:1.125rem;margin-top:1.5rem}}.hero__body:empty{display:none}.hero__media{max-width:var(--lc-hero-media-width, 23rem);margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){.hero__media{margin-top:2.5rem}}.hero__media:empty{display:none}.hero--slim .hero__media{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim .hero__media{margin-top:1.25rem}}.hero--light .hero__media{border-top-color:#0000001f}@media(min-width:1024px){.hero__inner:has(>.hero__media:not(:empty)){display:grid;grid-template-columns:minmax(0,1fr) var(--lc-hero-media-width, 23rem);column-gap:3.5rem;align-items:center}.hero__inner:has(>.hero__media:not(:empty))>.hero__media{max-width:none;margin-top:0;padding-top:0;border-top:0}}:host ::ng-deep [hero-footer]{display:grid;grid-template-columns:1fr;gap:1rem;margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(2,1fr);margin-top:2.5rem}}@media(min-width:1024px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(auto-fit,minmax(10rem,1fr));margin-top:3rem}}\n"],
89
125
  changeDetection: i0.ChangeDetectionStrategy.OnPush
90
126
  });
91
127
  };
@@ -100,8 +136,8 @@ i0.ɵɵngDeclareClassMetadata({
100
136
  selector: "lc-hero",
101
137
  standalone: true,
102
138
  changeDetection: ChangeDetectionStrategy.OnPush,
103
- template: "<div [class]=\"heroClasses()\">\n <div class=\"hero__inner\">\n <div class=\"hero__content\">\n @if (label()) {\n <span class=\"hero__label\">{{ label() }}</span>\n }\n <h1 class=\"hero__title\">{{ title() }}</h1>\n <div class=\"hero__body\">\n <ng-content></ng-content>\n </div>\n </div>\n <div class=\"hero__media\">\n <ng-content select=\"[hero-media]\"></ng-content>\n </div>\n </div>\n <ng-content select=\"[hero-footer]\"></ng-content>\n</div>\n",
104
- styles: [":host{display:block;width:100%}.hero{position:relative;display:flex;flex-direction:column;justify-content:space-between;color:#fff;overflow:hidden}.hero--primary{background:linear-gradient(135deg,#0d353d,#208497)}.hero--secondary{background:linear-gradient(135deg,#2d3c46,#7097af)}.hero--neutral{background:linear-gradient(135deg,#111827,#4b5563)}.hero--success{background:linear-gradient(135deg,#5f7150,#8ea475)}.hero--info{background:linear-gradient(135deg,#27445c,#3b6588)}.hero--warning{background:linear-gradient(135deg,#9b6b2b,#e1a040)}.hero--accent-orange{background:linear-gradient(135deg,#7a3a10,#df792e)}.hero--accent-purple{background:linear-gradient(135deg,#3a2a52,#866aa0)}.hero--accent-violet{background:linear-gradient(135deg,#221a42,#4e3f84)}.hero--sm{padding:1.5rem 1.25rem}@media(min-width:640px){.hero--sm{padding:2rem}}@media(min-width:1024px){.hero--sm{padding:2.5rem 3rem}}.hero--md{padding:2rem 1.25rem}@media(min-width:640px){.hero--md{padding:2.5rem 2rem}}@media(min-width:1024px){.hero--md{padding:3rem 3.5rem}}.hero--lg{padding:2.5rem 1.25rem}@media(min-width:640px){.hero--lg{padding:3rem 2.5rem}}@media(min-width:1024px){.hero--lg{padding:4rem}}.hero--radius-none{border-radius:var(--border-radius-none)}.hero--radius-sm{border-radius:var(--border-radius-sm)}.hero--radius-md{border-radius:var(--border-radius-md)}.hero--radius-lg{border-radius:var(--border-radius-lg)}.hero--slim{padding:1rem 1.25rem!important}@media(min-width:640px){.hero--slim{padding:1.25rem 2rem!important}}@media(min-width:1024px){.hero--slim{padding:1.5rem 3rem!important}}.hero--slim .hero__title{font-size:1.125rem!important;font-weight:700;line-height:1.3}@media(min-width:640px){.hero--slim .hero__title{font-size:1.25rem!important}}@media(min-width:1024px){.hero--slim .hero__title{font-size:1.5rem!important}}.hero--slim .hero__label{font-size:.5625rem;margin-bottom:.375rem;letter-spacing:.12em}@media(min-width:640px){.hero--slim .hero__label{font-size:.625rem;margin-bottom:.5rem}}.hero--slim .hero__body{margin-top:.5rem;font-size:.8125rem}@media(min-width:640px){.hero--slim .hero__body{font-size:.875rem;margin-top:.625rem}}.hero--slim ::ng-deep [hero-footer]{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim ::ng-deep [hero-footer]{margin-top:1.25rem}}.hero--light{color:#1f2937}.hero--light .hero__label{opacity:.7}.hero--light .hero__body{opacity:.75}.hero--light ::ng-deep [hero-footer]{border-top-color:#0000001f}.hero--light.hero--primary{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--secondary{background:linear-gradient(135deg,#e0e7ff,#eef2ff)}.hero--light.hero--neutral{background:linear-gradient(135deg,#e5e7eb,#f9fafb)}.hero--light.hero--success{background:linear-gradient(135deg,#d1fae5,#ecfdf5)}.hero--light.hero--info{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--warning{background:linear-gradient(135deg,#fef3c7,#fffbeb)}.hero--light.hero--accent-orange{background:linear-gradient(135deg,#ffedd5,#fff7ed)}.hero--light.hero--accent-purple{background:linear-gradient(135deg,#e9d5ff,#f5f3ff)}.hero--light.hero--accent-violet{background:linear-gradient(135deg,#ddd6fe,#ede9fe)}@media(min-width:1024px){.hero--content-lg.hero--sm{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--md{padding-inline:max(3.5rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--lg{padding-inline:max(4rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--slim{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)!important}}.hero__inner{flex:1}.hero__content{min-width:0}.hero__label{display:inline-block;font-size:.6875rem;font-weight:600;letter-spacing:.15em;text-transform:uppercase;opacity:.8;margin-bottom:.75rem}@media(min-width:640px){.hero__label{font-size:.75rem;margin-bottom:1rem}}.hero__title{margin:0;font-weight:800;line-height:1.1;color:inherit}.hero--sm .hero__title{font-size:1.5rem}@media(min-width:640px){.hero--sm .hero__title{font-size:1.75rem}}@media(min-width:1024px){.hero--sm .hero__title{font-size:2rem}}.hero--md .hero__title{font-size:1.75rem}@media(min-width:640px){.hero--md .hero__title{font-size:2.25rem}}@media(min-width:1024px){.hero--md .hero__title{font-size:2.75rem}}.hero--lg .hero__title{font-size:2rem}@media(min-width:640px){.hero--lg .hero__title{font-size:2.75rem}}@media(min-width:1024px){.hero--lg .hero__title{font-size:3.5rem}}.hero__body{margin-top:1rem;font-size:.875rem;line-height:1.6;opacity:.85;max-width:48rem}@media(min-width:640px){.hero__body{font-size:1rem;margin-top:1.25rem}}@media(min-width:1024px){.hero__body{font-size:1.125rem;margin-top:1.5rem}}.hero__body:empty{display:none}.hero__media{max-width:var(--lc-hero-media-width, 23rem);margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){.hero__media{margin-top:2.5rem}}.hero__media:empty{display:none}.hero--slim .hero__media{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim .hero__media{margin-top:1.25rem}}.hero--light .hero__media{border-top-color:#0000001f}@media(min-width:1024px){.hero__inner:has(>.hero__media:not(:empty)){display:grid;grid-template-columns:minmax(0,1fr) var(--lc-hero-media-width, 23rem);column-gap:3.5rem;align-items:center}.hero__inner:has(>.hero__media:not(:empty))>.hero__media{max-width:none;margin-top:0;padding-top:0;border-top:0}}:host ::ng-deep [hero-footer]{display:grid;grid-template-columns:1fr;gap:1rem;margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(2,1fr);margin-top:2.5rem}}@media(min-width:1024px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(auto-fit,minmax(10rem,1fr));margin-top:3rem}}\n"]
139
+ template: "<div [class]=\"heroClasses()\">\n <div class=\"hero__inner\">\n <div class=\"hero__content\">\n @if (label()) {\n <span #labelEl class=\"hero__label\">{{ label() }}</span>\n }\n <!-- 3.36: the element follows `headingLevel`; the look is the same at every level. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 #titleEl class=\"hero__title\">{{ title() }}</h2>\n }\n @case (3) {\n <h3 #titleEl class=\"hero__title\">{{ title() }}</h3>\n }\n @case (4) {\n <h4 #titleEl class=\"hero__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 #titleEl class=\"hero__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 #titleEl class=\"hero__title\">{{ title() }}</h6>\n }\n @default {\n <h1 #titleEl class=\"hero__title\">{{ title() }}</h1>\n }\n }\n <div class=\"hero__body\">\n <ng-content></ng-content>\n </div>\n </div>\n <div class=\"hero__media\">\n <ng-content select=\"[hero-media]\"></ng-content>\n </div>\n </div>\n <ng-content select=\"[hero-footer]\"></ng-content>\n</div>\n",
140
+ styles: [":host{display:block;width:100%}.hero{position:relative;display:flex;flex-direction:column;justify-content:space-between;color:#fff;overflow:hidden}.hero--primary{background:linear-gradient(135deg,#0d353d,#208497)}.hero--secondary{background:linear-gradient(135deg,#2d3c46,#7097af)}.hero--neutral{background:linear-gradient(135deg,#111827,#4b5563)}.hero--success{background:linear-gradient(135deg,#5f7150,#8ea475)}.hero--info{background:linear-gradient(135deg,#27445c,#3b6588)}.hero--warning{background:linear-gradient(135deg,#9b6b2b,#e1a040)}.hero--accent-orange{background:linear-gradient(135deg,#7a3a10,#df792e)}.hero--accent-purple{background:linear-gradient(135deg,#3a2a52,#866aa0)}.hero--accent-violet{background:linear-gradient(135deg,#221a42,#4e3f84)}.hero--sm{padding:1.5rem 1.25rem}@media(min-width:640px){.hero--sm{padding:2rem}}@media(min-width:1024px){.hero--sm{padding:2.5rem 3rem}}.hero--md{padding:2rem 1.25rem}@media(min-width:640px){.hero--md{padding:2.5rem 2rem}}@media(min-width:1024px){.hero--md{padding:3rem 3.5rem}}.hero--lg{padding:2.5rem 1.25rem}@media(min-width:640px){.hero--lg{padding:3rem 2.5rem}}@media(min-width:1024px){.hero--lg{padding:4rem}}.hero--radius-none{border-radius:var(--border-radius-none)}.hero--radius-sm{border-radius:var(--border-radius-sm)}.hero--radius-md{border-radius:var(--border-radius-md)}.hero--radius-lg{border-radius:var(--border-radius-lg)}.hero--slim{padding:1rem 1.25rem!important}@media(min-width:640px){.hero--slim{padding:1.25rem 2rem!important}}@media(min-width:1024px){.hero--slim{padding:1.5rem 3rem!important}}.hero--slim .hero__title{font-size:1.125rem!important;font-weight:700;line-height:1.3}@media(min-width:640px){.hero--slim .hero__title{font-size:1.25rem!important}}@media(min-width:1024px){.hero--slim .hero__title{font-size:1.5rem!important}}.hero--slim .hero__label{font-size:.5625rem;margin-bottom:.375rem;letter-spacing:.12em}@media(min-width:640px){.hero--slim .hero__label{font-size:.625rem;margin-bottom:.5rem}}.hero--slim .hero__body{margin-top:.5rem;font-size:.8125rem}@media(min-width:640px){.hero--slim .hero__body{font-size:.875rem;margin-top:.625rem}}.hero--slim ::ng-deep [hero-footer]{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim ::ng-deep [hero-footer]{margin-top:1.25rem}}.hero--light{color:#1f2937}.hero--light .hero__label{opacity:.7}.hero--light .hero__body{opacity:.75}.hero--light ::ng-deep [hero-footer]{border-top-color:#0000001f}.hero--light.hero--primary{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--secondary{background:linear-gradient(135deg,#e0e7ff,#eef2ff)}.hero--light.hero--neutral{background:linear-gradient(135deg,#e5e7eb,#f9fafb)}.hero--light.hero--success{background:linear-gradient(135deg,#d1fae5,#ecfdf5)}.hero--light.hero--info{background:linear-gradient(135deg,#dbeafe,#eff6ff)}.hero--light.hero--warning{background:linear-gradient(135deg,#fef3c7,#fffbeb)}.hero--light.hero--accent-orange{background:linear-gradient(135deg,#ffedd5,#fff7ed)}.hero--light.hero--accent-purple{background:linear-gradient(135deg,#e9d5ff,#f5f3ff)}.hero--light.hero--accent-violet{background:linear-gradient(135deg,#ddd6fe,#ede9fe)}@media(min-width:1024px){.hero--content-lg.hero--sm{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--md{padding-inline:max(3.5rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--lg{padding-inline:max(4rem,(100% - 1024px) / 2 + 3rem)}.hero--content-lg.hero--slim{padding-inline:max(3rem,(100% - 1024px) / 2 + 3rem)!important}}.hero__inner{flex:1}.hero__content{min-width:0}.hero__label{display:inline-block;font-size:.6875rem;font-weight:600;letter-spacing:.15em;text-transform:uppercase;opacity:.8;margin-bottom:.75rem}@media(min-width:640px){.hero__label{font-size:.75rem;margin-bottom:1rem}}.hero__title{margin:0;font-weight:800;line-height:1.1;color:inherit}.hero--sm .hero__title{font-size:1.5rem}@media(min-width:640px){.hero--sm .hero__title{font-size:1.75rem}}@media(min-width:1024px){.hero--sm .hero__title{font-size:2rem}}.hero--md .hero__title{font-size:1.75rem}@media(min-width:640px){.hero--md .hero__title{font-size:2.25rem}}@media(min-width:1024px){.hero--md .hero__title{font-size:2.75rem}}.hero--lg .hero__title{font-size:2rem}@media(min-width:640px){.hero--lg .hero__title{font-size:2.75rem}}@media(min-width:1024px){.hero--lg .hero__title{font-size:3.5rem}}.hero__title:focus-visible{outline:2px solid currentColor;outline-offset:2px;box-shadow:0 0 0 2px #111827;border-radius:var(--border-radius-sm)}.hero--light .hero__title:focus-visible{box-shadow:0 0 0 2px #fff}@media(forced-colors:active){.hero__title:focus-visible{outline-color:Highlight;box-shadow:none}}.hero__body{margin-top:1rem;font-size:.875rem;line-height:1.6;opacity:.85;max-width:48rem}@media(min-width:640px){.hero__body{font-size:1rem;margin-top:1.25rem}}@media(min-width:1024px){.hero__body{font-size:1.125rem;margin-top:1.5rem}}.hero__body:empty{display:none}.hero__media{max-width:var(--lc-hero-media-width, 23rem);margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){.hero__media{margin-top:2.5rem}}.hero__media:empty{display:none}.hero--slim .hero__media{margin-top:1rem;padding-top:.75rem}@media(min-width:640px){.hero--slim .hero__media{margin-top:1.25rem}}.hero--light .hero__media{border-top-color:#0000001f}@media(min-width:1024px){.hero__inner:has(>.hero__media:not(:empty)){display:grid;grid-template-columns:minmax(0,1fr) var(--lc-hero-media-width, 23rem);column-gap:3.5rem;align-items:center}.hero__inner:has(>.hero__media:not(:empty))>.hero__media{max-width:none;margin-top:0;padding-top:0;border-top:0}}:host ::ng-deep [hero-footer]{display:grid;grid-template-columns:1fr;gap:1rem;margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.2)}@media(min-width:640px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(2,1fr);margin-top:2.5rem}}@media(min-width:1024px){:host ::ng-deep [hero-footer]{grid-template-columns:repeat(auto-fit,minmax(10rem,1fr));margin-top:3rem}}\n"]
105
141
  }]
106
142
  }],
107
143
  propDecorators: {
@@ -160,6 +196,22 @@ i0.ɵɵngDeclareClassMetadata({
160
196
  alias: "contentWidth",
161
197
  required: false
162
198
  }]
199
+ }],
200
+ headingLevel: [{
201
+ type: i0.Input,
202
+ args: [{
203
+ isSignal: true,
204
+ alias: "headingLevel",
205
+ required: false
206
+ }]
207
+ }],
208
+ titleEl: [{
209
+ type: i0.ViewChild,
210
+ args: ["titleEl", { isSignal: true }]
211
+ }],
212
+ labelEl: [{
213
+ type: i0.ViewChild,
214
+ args: ["labelEl", { isSignal: true }]
163
215
  }]
164
216
  }
165
217
  });
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-hero.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/hero/hero.component.ts","../../../../libs/angular-ui-kit/src/lib/hero/hero.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n} from '@angular/core';\n\nexport type HeroColor =\n | 'primary'\n | 'secondary'\n | 'neutral'\n | 'success'\n | 'info'\n | 'warning'\n | 'accent-orange'\n | 'accent-purple'\n | 'accent-violet';\n\nexport type HeroSize = 'sm' | 'md' | 'lg';\nexport type HeroVariant = 'default' | 'slim' | 'light';\n\n/**\n * Width of the hero's content box.\n * - `full`: the content runs to the hero's own padding (default, as before 3.4)\n * - `lg`: from 1024 px the content sits on the `lc-container` `lg` page column\n * (1024 px minus its 48 px sides, so at most 928 px), centred, while the\n * background stays full-bleed\n */\nexport type HeroContentWidth = 'full' | 'lg';\n\n/**\n * Hero component for prominent page headers with gradient backgrounds.\n *\n * Features:\n * - Color gradient variants (primary, secondary, success, warning, info, neutral, accent-orange, accent-purple, accent-violet)\n * - 3 visual variants: default (dark gradient), slim (compact banner), light (pastel gradient with dark text)\n * - Size options (sm, md, lg)\n * - Optional label text above the title\n * - Configurable border radius\n * - Content projection for body and footer sections\n * - Footer slot for metadata, stats, or CTAs\n * - Media slot (`[hero-media]`) for a visual: below the text on small screens,\n * beside it from 1024 px, never under the body's 85 % opacity\n * - Content width `lg`: a full-bleed band whose text lines up with the page column\n *\n * @example\n * ```html\n * <lc-hero label=\"MY APP\" title=\"Welcome\" color=\"primary\" variant=\"slim\">\n * <p>Description text</p>\n * <div hero-footer>Status: Active</div>\n * </lc-hero>\n *\n * <lc-hero title=\"Everything in view\" borderRadius=\"none\" contentWidth=\"lg\">\n * <p>Description text</p>\n * <lc-card hero-media variant=\"elevated\">…</lc-card>\n * </lc-hero>\n * ```\n */\n@Component({\n selector: 'lc-hero',\n standalone: true,\n templateUrl: './hero.component.html',\n styleUrls: ['./hero.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class HeroComponent {\n /**\n * Small uppercase label displayed above the title.\n */\n label = input<string | undefined>(undefined);\n\n /**\n * Main headline text.\n */\n title = input.required<string>();\n\n /**\n * Color theme for the gradient background.\n * @default 'primary'\n */\n color = input<HeroColor>('primary');\n\n /**\n * Size variant controlling padding and font sizes.\n * @default 'md'\n */\n size = input<HeroSize>('md');\n\n /**\n * Border radius of the hero container.\n * @default 'lg'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg'>('lg');\n\n /**\n * Visual variant.\n * - `default`: dark gradient with white text\n * - `slim`: compact single-line banner\n * - `light`: softer pastel gradient with dark text\n * @default 'default'\n */\n variant = input<HeroVariant>('default');\n\n /**\n * Width of the content box. `lg` keeps the background full-bleed and, from\n * 1024 px, puts the label, title, body, media and footer on the 1024 px page\n * column (the `lc-container` `lg` content box), never closer to the edge than\n * the size padding. Use it for a full-width band (`borderRadius=\"none\"`) whose\n * text should line up with the page content below.\n * @default 'full'\n */\n contentWidth = input<HeroContentWidth>('full');\n\n protected heroClasses = computed(() => {\n return [\n 'hero',\n `hero--${this.color()}`,\n `hero--${this.size()}`,\n `hero--radius-${this.borderRadius()}`,\n `hero--${this.variant()}`,\n `hero--content-${this.contentWidth()}`,\n ].join(' ');\n });\n}\n","<div [class]=\"heroClasses()\">\n <div class=\"hero__inner\">\n <div class=\"hero__content\">\n @if (label()) {\n <span class=\"hero__label\">{{ label() }}</span>\n }\n <h1 class=\"hero__title\">{{ title() }}</h1>\n <div class=\"hero__body\">\n <ng-content></ng-content>\n </div>\n </div>\n <div class=\"hero__media\">\n <ng-content select=\"[hero-media]\"></ng-content>\n </div>\n </div>\n <ng-content select=\"[hero-footer]\"></ng-content>\n</div>\n"],"mappings":";;AAiEA,IAAa,gBAAb,MAAa,cAAa;CAIxB,QAAQ,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAK5C,QAAQ,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAMhC,QAAQ,MAAiB,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAMnC,OAAO,MAAgB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAM5B,eAAe,MAAmC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CASvD,UAAU,MAAmB,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAUvC,eAAe,MAAwB,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEpC,cAAc,eAAc;EACpC,OAAO;GACL;GACA,SAAS,KAAK,MAAK;GACnB,SAAS,KAAK,KAAI;GAClB,gBAAgB,KAAK,aAAY;GACjC,SAAS,KAAK,QAAO;GACrB,iBAAiB,KAAK,aAAY;EACnC,CAAA,CAAC,KAAK,GAAG;CACZ,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAzDS,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAa,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAb,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAa,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UCjE1B;EAiBA,QAAA,CAAA,unLAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;ADgDa,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAa,YAAA,CAAA;EAPzB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAS,YACP;GAAI,iBAGC,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,unLAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-hero.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/hero/hero.component.ts","../../../../libs/angular-ui-kit/src/lib/hero/hero.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n computed,\n viewChild,\n} from '@angular/core';\n\nexport type HeroColor =\n | 'primary'\n | 'secondary'\n | 'neutral'\n | 'success'\n | 'info'\n | 'warning'\n | 'accent-orange'\n | 'accent-purple'\n | 'accent-violet';\n\nexport type HeroSize = 'sm' | 'md' | 'lg';\nexport type HeroVariant = 'default' | 'slim' | 'light';\n\n/**\n * Width of the hero's content box.\n * - `full`: the content runs to the hero's own padding (default, as before 3.4)\n * - `lg`: from 1024 px the content sits on the `lc-container` `lg` page column\n * (1024 px minus its 48 px sides, so at most 928 px), centred, while the\n * background stays full-bleed\n */\nexport type HeroContentWidth = 'full' | 'lg';\n\n/** Heading level of `lc-hero`'s title (3.36): `h1`–`h6`, the same look at every level. */\nexport type HeroHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\n/** Ids of the title and label, set by `focusTitle()` (module counter, only at the call). */\nlet nextHeroId = 0;\n\n/**\n * Hero component for prominent page headers with gradient backgrounds.\n *\n * Features:\n * - Color gradient variants (primary, secondary, success, warning, info, neutral, accent-orange, accent-purple, accent-violet)\n * - 3 visual variants: default (dark gradient), slim (compact banner), light (pastel gradient with dark text)\n * - Size options (sm, md, lg)\n * - Optional label text above the title\n * - Configurable border radius\n * - Content projection for body and footer sections\n * - Footer slot for metadata, stats, or CTAs\n * - Media slot (`[hero-media]`) for a visual: below the text on small screens,\n * beside it from 1024 px, never under the body's 85 % opacity\n * - Content width `lg`: a full-bleed band whose text lines up with the page column\n * - `headingLevel` (3.36): the title as `h1`–`h6`, the same look at every level\n * - `focusTitle()` (3.36): moves focus to the title (`tabindex=\"-1\"`, out of the\n * Tab order) after an action replaced the stage (WCAG 2.4.3)\n *\n * @example\n * ```html\n * <lc-hero label=\"MY APP\" title=\"Welcome\" color=\"primary\" variant=\"slim\">\n * <p>Description text</p>\n * <div hero-footer>Status: Active</div>\n * </lc-hero>\n *\n * <lc-hero title=\"Everything in view\" borderRadius=\"none\" contentWidth=\"lg\">\n * <p>Description text</p>\n * <lc-card hero-media variant=\"elevated\">…</lc-card>\n * </lc-hero>\n * ```\n */\n@Component({\n selector: 'lc-hero',\n standalone: true,\n templateUrl: './hero.component.html',\n styleUrls: ['./hero.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class HeroComponent {\n /**\n * Small uppercase label displayed above the title.\n */\n label = input<string | undefined>(undefined);\n\n /**\n * Main headline text.\n */\n title = input.required<string>();\n\n /**\n * Color theme for the gradient background.\n * @default 'primary'\n */\n color = input<HeroColor>('primary');\n\n /**\n * Size variant controlling padding and font sizes.\n * @default 'md'\n */\n size = input<HeroSize>('md');\n\n /**\n * Border radius of the hero container.\n * @default 'lg'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg'>('lg');\n\n /**\n * Visual variant.\n * - `default`: dark gradient with white text\n * - `slim`: compact single-line banner\n * - `light`: softer pastel gradient with dark text\n * @default 'default'\n */\n variant = input<HeroVariant>('default');\n\n /**\n * Width of the content box. `lg` keeps the background full-bleed and, from\n * 1024 px, puts the label, title, body, media and footer on the 1024 px page\n * column (the `lc-container` `lg` content box), never closer to the edge than\n * the size padding. Use it for a full-width band (`borderRadius=\"none\"`) whose\n * text should line up with the page content below.\n * @default 'full'\n */\n contentWidth = input<HeroContentWidth>('full');\n\n /**\n * Element of the title (`h1`–`h6`), so the hero fits the page's outline\n * (3.36, cockpit row 1). Only the element changes: size, weight, line height\n * and spacing are the same at every level, `size`, `variant` and breakpoint.\n * Use 2 under an `lc-page-header`, which holds the page's `h1`.\n * @default 1\n */\n headingLevel = input<HeroHeadingLevel>(1);\n\n private readonly titleEl = viewChild<ElementRef<HTMLElement>>('titleEl');\n private readonly labelEl = viewChild<ElementRef<HTMLElement>>('labelEl');\n\n /**\n * Moves the focus to the title (3.36, monatsstand row 1), e.g. after an\n * action replaced the stage and the button that was used is gone, so focus\n * doesn't fall back to the page (WCAG 2.4.3). Gives the title\n * `tabindex=\"-1\"` first: it stays out of the Tab order. With a `label`, the\n * title is named \"label title\" (`aria-labelledby` on both, ids set at the\n * call), so the label is read before it. Call it after the new state has\n * rendered, e.g. `afterNextRender(() => this.hero().focusTitle(), { injector })`\n * with `hero = viewChild.required(HeroComponent)`, as\n * `lc-next-step-card.focusTitle()`. Without a call nothing changes.\n */\n focusTitle(): void {\n const title = this.titleEl()?.nativeElement;\n if (!title) return;\n const label = this.labelEl()?.nativeElement;\n if (label) {\n title.id ||= `lc-hero-title-${++nextHeroId}`;\n label.id ||= `lc-hero-label-${++nextHeroId}`;\n title.setAttribute('aria-labelledby', `${label.id} ${title.id}`);\n } else {\n title.removeAttribute('aria-labelledby');\n }\n title.setAttribute('tabindex', '-1');\n title.focus();\n }\n\n protected heroClasses = computed(() => {\n return [\n 'hero',\n `hero--${this.color()}`,\n `hero--${this.size()}`,\n `hero--radius-${this.borderRadius()}`,\n `hero--${this.variant()}`,\n `hero--content-${this.contentWidth()}`,\n ].join(' ');\n });\n}\n","<div [class]=\"heroClasses()\">\n <div class=\"hero__inner\">\n <div class=\"hero__content\">\n @if (label()) {\n <span #labelEl class=\"hero__label\">{{ label() }}</span>\n }\n <!-- 3.36: the element follows `headingLevel`; the look is the same at every level. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 #titleEl class=\"hero__title\">{{ title() }}</h2>\n }\n @case (3) {\n <h3 #titleEl class=\"hero__title\">{{ title() }}</h3>\n }\n @case (4) {\n <h4 #titleEl class=\"hero__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 #titleEl class=\"hero__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 #titleEl class=\"hero__title\">{{ title() }}</h6>\n }\n @default {\n <h1 #titleEl class=\"hero__title\">{{ title() }}</h1>\n }\n }\n <div class=\"hero__body\">\n <ng-content></ng-content>\n </div>\n </div>\n <div class=\"hero__media\">\n <ng-content select=\"[hero-media]\"></ng-content>\n </div>\n </div>\n <ng-content select=\"[hero-footer]\"></ng-content>\n</div>\n"],"mappings":"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"}
@@ -74,6 +74,11 @@ i0.ɵɵngDeclareClassMetadata({
74
74
  args: [{ providedIn: "root" }]
75
75
  }]
76
76
  });
77
- export { OverlayInsetsService, OverlayStackService };
77
+ const FOCUSABLE = "button:not([disabled]), [href], input:not([disabled]):not([type=hidden]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])";
78
+ function focusReturnTarget(target) {
79
+ target.focus();
80
+ if (target.ownerDocument.activeElement !== target && target.localName.includes("-")) target.querySelector(FOCUSABLE)?.focus();
81
+ }
82
+ export { OverlayInsetsService, OverlayStackService, focusReturnTarget };
78
83
 
79
84
  //# sourceMappingURL=lifecorner-angular-ui-kit-internal-overlay.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-internal-overlay.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/internal/overlay/overlay-stack.service.ts","../../../../libs/angular-ui-kit/src/lib/internal/overlay/overlay-insets.service.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\n/**\n * Keeps the open overlays (modal, drawer, popover, menu, tooltip, …) in\n * opening order so that a dismiss gesture — Escape, click outside — reaches\n * only the top-most one instead of closing every open layer at once.\n *\n * Every overlay pushes its id when it opens and removes it when it closes or\n * is destroyed. Internal to the library; not part of the public API.\n */\n@Injectable({ providedIn: 'root' })\nexport class OverlayStackService {\n private readonly stack: string[] = [];\n\n /**\n * Events already consumed by an overlay. All overlays listen on `document`,\n * where listeners fire in registration order — not z-order — so by the time a\n * lower overlay's listener runs, the top one may already have closed and left\n * the stack, making the lower one \"top\" for the very same event.\n */\n private readonly claimed = new WeakSet<Event>();\n\n /** Register `id` as the new top-most overlay (re-pushing moves it to the top). */\n push(id: string): void {\n this.remove(id);\n this.stack.push(id);\n }\n\n remove(id: string): void {\n const index = this.stack.indexOf(id);\n if (index !== -1) this.stack.splice(index, 1);\n }\n\n isTop(id: string): boolean {\n return this.stack.length > 0 && this.stack[this.stack.length - 1] === id;\n }\n\n /**\n * Whether overlay `id` may act on this dismiss event. True only for the\n * top-most overlay and only once per event; the caller then closes and, for\n * keyboard events, stops propagation so nothing outside the stack sees it.\n */\n claim(id: string, event: Event): boolean {\n if (this.claimed.has(event) || !this.isTop(id)) return false;\n this.claimed.add(event);\n return true;\n }\n}\n","import { Injectable, signal } from '@angular/core';\n\n/**\n * The space a fixed overlay keeps free at the top and the bottom of the\n * viewport, as CSS lengths (3.28, AS-34). `lc-app-shell` writes them in the\n * browser: `top` is its header's bottom edge, `bottom` the shown bottom bar's\n * top edge, measured from the viewport's bottom (or the safe-area insets when\n * there is no header or no bar). `lc-toast-outlet` reads them, so its stacks\n * clear the header and the bar even when the app renders the outlet outside\n * the shell (no custom property can inherit there).\n *\n * `null` means \"no shell\": on the server, before the shell has measured,\n * without ResizeObserver and after the shell is destroyed. Internal to the\n * library; not part of the public API and without a semver guarantee.\n */\n@Injectable({ providedIn: 'root' })\nexport class OverlayInsetsService {\n /** CSS length kept free above top-edge overlays, e.g. `'56px'`. */\n readonly top = signal<string | null>(null);\n\n /** CSS length kept free below bottom-edge overlays, e.g. `'102px'`. */\n readonly bottom = signal<string | null>(null);\n}\n"],"mappings":";;AAWA,IAAa,sBAAb,MAAa,oBAAmB;CACb,QAAkB,CAAA;CAQlB,0BAAU,IAAI,QAAO;CAGtC,KAAK,IAAU;EACb,KAAK,OAAO,EAAE;EACd,KAAK,MAAM,KAAK,EAAE;CACpB;CAEA,OAAO,IAAU;EACf,MAAM,QAAQ,KAAK,MAAM,QAAQ,EAAE;EACnC,IAAI,UAAU,IAAI,KAAK,MAAM,OAAO,OAAO,CAAC;CAC9C;CAEA,MAAM,IAAU;EACd,OAAO,KAAK,MAAM,SAAS,KAAK,KAAK,MAAM,KAAK,MAAM,SAAS,OAAO;CACxE;CAOA,MAAM,IAAY,OAAY;EAC5B,IAAI,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC,KAAK,MAAM,EAAE,GAAG,OAAO;EACvD,KAAK,QAAQ,IAAI,KAAK;EACtB,OAAO;CACT;CAnCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,YADN;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAD/B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACMlC,IAAa,uBAAb,MAAa,qBAAoB;CAEtB,MAAM,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,SAAS,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CALlC,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,YADP;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EADhC,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-internal-overlay.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/internal/overlay/overlay-stack.service.ts","../../../../libs/angular-ui-kit/src/lib/internal/overlay/overlay-insets.service.ts","../../../../libs/angular-ui-kit/src/lib/internal/overlay/focus-return.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\n/**\n * Keeps the open overlays (modal, drawer, popover, menu, tooltip, …) in\n * opening order so that a dismiss gesture — Escape, click outside — reaches\n * only the top-most one instead of closing every open layer at once.\n *\n * Every overlay pushes its id when it opens and removes it when it closes or\n * is destroyed. Internal to the library; not part of the public API.\n */\n@Injectable({ providedIn: 'root' })\nexport class OverlayStackService {\n private readonly stack: string[] = [];\n\n /**\n * Events already consumed by an overlay. All overlays listen on `document`,\n * where listeners fire in registration order — not z-order — so by the time a\n * lower overlay's listener runs, the top one may already have closed and left\n * the stack, making the lower one \"top\" for the very same event.\n */\n private readonly claimed = new WeakSet<Event>();\n\n /** Register `id` as the new top-most overlay (re-pushing moves it to the top). */\n push(id: string): void {\n this.remove(id);\n this.stack.push(id);\n }\n\n remove(id: string): void {\n const index = this.stack.indexOf(id);\n if (index !== -1) this.stack.splice(index, 1);\n }\n\n isTop(id: string): boolean {\n return this.stack.length > 0 && this.stack[this.stack.length - 1] === id;\n }\n\n /**\n * Whether overlay `id` may act on this dismiss event. True only for the\n * top-most overlay and only once per event; the caller then closes and, for\n * keyboard events, stops propagation so nothing outside the stack sees it.\n */\n claim(id: string, event: Event): boolean {\n if (this.claimed.has(event) || !this.isTop(id)) return false;\n this.claimed.add(event);\n return true;\n }\n}\n","import { Injectable, signal } from '@angular/core';\n\n/**\n * The space a fixed overlay keeps free at the top and the bottom of the\n * viewport, as CSS lengths (3.28, AS-34). `lc-app-shell` writes them in the\n * browser: `top` is its header's bottom edge, `bottom` the shown bottom bar's\n * top edge, measured from the viewport's bottom (or the safe-area insets when\n * there is no header or no bar). `lc-toast-outlet` reads them, so its stacks\n * clear the header and the bar even when the app renders the outlet outside\n * the shell (no custom property can inherit there).\n *\n * `null` means \"no shell\": on the server, before the shell has measured,\n * without ResizeObserver and after the shell is destroyed. Internal to the\n * library; not part of the public API and without a semver guarantee.\n */\n@Injectable({ providedIn: 'root' })\nexport class OverlayInsetsService {\n /** CSS length kept free above top-edge overlays, e.g. `'56px'`. */\n readonly top = signal<string | null>(null);\n\n /** CSS length kept free below bottom-edge overlays, e.g. `'102px'`. */\n readonly bottom = signal<string | null>(null);\n}\n","/**\n * What a custom-element `returnFocusTo` host (`<lc-button>`) hands the focus\n * to when it can't take it itself.\n */\nconst FOCUSABLE =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=hidden]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Gives the focus to an overlay's `returnFocusTo` element (3.34 in\n * `lc-modal`, shared since 3.35 with `lc-drawer` and `lc-sidenav`). A\n * custom-element host that can't take focus (`<lc-button>`) hands it to its\n * first focusable descendant. An element that can't take focus at all leaves\n * the focus where it is.\n */\nexport function focusReturnTarget(target: HTMLElement): void {\n target.focus();\n if (target.ownerDocument.activeElement !== target && target.localName.includes('-')) {\n target.querySelector<HTMLElement>(FOCUSABLE)?.focus();\n }\n}\n"],"mappings":";;AAWA,IAAa,sBAAb,MAAa,oBAAmB;CACb,QAAkB,CAAA;CAQlB,0BAAU,IAAI,QAAO;CAGtC,KAAK,IAAU;EACb,KAAK,OAAO,EAAE;EACd,KAAK,MAAM,KAAK,EAAE;CACpB;CAEA,OAAO,IAAU;EACf,MAAM,QAAQ,KAAK,MAAM,QAAQ,EAAE;EACnC,IAAI,UAAU,IAAI,KAAK,MAAM,OAAO,OAAO,CAAC;CAC9C;CAEA,MAAM,IAAU;EACd,OAAO,KAAK,MAAM,SAAS,KAAK,KAAK,MAAM,KAAK,MAAM,SAAS,OAAO;CACxE;CAOA,MAAM,IAAY,OAAY;EAC5B,IAAI,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC,KAAK,MAAM,EAAE,GAAG,OAAO;EACvD,KAAK,QAAQ,IAAI,KAAK;EACtB,OAAO;CACT;CAnCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,YADN;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAD/B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACMlC,IAAa,uBAAb,MAAa,qBAAoB;CAEtB,MAAM,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,SAAS,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CALlC,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,YADP;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EADhC,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACXlC,MAAM,YACJ;AASF,SAAgB,kBAAkB,QAAmB;CACnD,OAAO,MAAK;CACZ,IAAI,OAAO,cAAc,kBAAkB,UAAU,OAAO,UAAU,SAAS,GAAG,GAChF,OAAO,cAA2B,SAAS,CAAC,EAAE,MAAK;AAEvD"}