@yuuvis/client-framework 2.10.3 → 2.11.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.
- package/autocomplete/lib/autocomplete.component.d.ts +4 -4
- package/autocomplete/lib/autocomplete.interface.d.ts +2 -2
- package/common/lib/services/index.d.ts +1 -0
- package/common/lib/services/layout-settings/layout-settings.service.d.ts +15 -0
- package/common/lib/services/theme/index.d.ts +3 -0
- package/common/lib/services/theme/theme.models.d.ts +16 -0
- package/common/lib/services/theme/theme.provider.d.ts +4 -0
- package/common/lib/services/theme/theme.service.d.ts +16 -0
- package/fesm2022/yuuvis-client-framework-common.mjs +213 -4
- package/fesm2022/yuuvis-client-framework-common.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-datepicker.mjs +1 -1
- package/fesm2022/yuuvis-client-framework-datepicker.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-forms.mjs +46 -21
- package/fesm2022/yuuvis-client-framework-forms.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-flavor.mjs +2 -2
- package/fesm2022/yuuvis-client-framework-object-flavor.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-relationship.mjs +135 -52
- package/fesm2022/yuuvis-client-framework-object-relationship.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-versions.mjs +4 -3
- package/fesm2022/yuuvis-client-framework-object-versions.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-query-list.mjs +5 -4
- package/fesm2022/yuuvis-client-framework-query-list.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-tile-list.mjs +20 -4
- package/fesm2022/yuuvis-client-framework-tile-list.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework.mjs +616 -98
- package/fesm2022/yuuvis-client-framework.mjs.map +1 -1
- package/forms/lib/elements/datetime/datetime.component.d.ts +0 -1
- package/forms/lib/elements/organization/organization.component.d.ts +1 -1
- package/forms/lib/elements/organization-set/organization-set.component.d.ts +1 -1
- package/index.d.ts +5 -2
- package/lib/config/index.d.ts +1 -0
- package/lib/config/session/index.d.ts +3 -0
- package/lib/config/session/session-activity-window-before-end.const.d.ts +43 -0
- package/lib/config/session/session-default-duration.const.d.ts +47 -0
- package/lib/config/session/session-popup-before-end.const.d.ts +47 -0
- package/lib/enums/channel-message.enum.d.ts +4 -0
- package/lib/enums/index.d.ts +1 -0
- package/lib/models/index.d.ts +2 -0
- package/lib/models/session/channel-payload.model.d.ts +5 -0
- package/lib/models/session/index.d.ts +1 -0
- package/lib/models/snack-bar/index.d.ts +3 -0
- package/lib/models/snack-bar/snack-bar-data.model.d.ts +6 -0
- package/lib/models/snack-bar/snack-bar-level.model.d.ts +1 -0
- package/lib/{services/snack-bar/snack-bar.interface.d.ts → models/snack-bar/snack-bar-options.model.d.ts} +1 -6
- package/lib/providers/index.d.ts +1 -0
- package/lib/providers/session/index.d.ts +1 -0
- package/lib/providers/session/provide-session.provider.d.ts +43 -0
- package/lib/services/index.d.ts +2 -2
- package/lib/services/session/session.service.d.ts +113 -0
- package/lib/services/snack-bar/snack-bar.service.d.ts +5 -5
- package/object-relationship/index.d.ts +1 -0
- package/object-relationship/lib/actions/add-relationship/add-relationship.component.d.ts +10 -0
- package/object-relationship/lib/actions/relationship-target-search/relationship-target-search.component.d.ts +17 -4
- package/object-relationship/lib/object-relationship.const.d.ts +0 -1
- package/object-versions/lib/object-versions.component.d.ts +1 -0
- package/package.json +8 -8
- package/query-list/lib/query-list.component.d.ts +8 -7
- package/tile-list/lib/tile-list/tile-list.component.d.ts +4 -2
- package/lib/assets/i18n/de.json +0 -202
- package/lib/assets/i18n/en.json +0 -202
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yuuvis-client-framework.mjs","sources":["../../../../../libs/yuuvis/client-framework/src/lib/yuuvis-client-framework.module.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-excluded-elements.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-focus-navigation-keys.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-focus-offset.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-focus-styles.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/halo-focus/halo-focus.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/snack-bar/snack-bar.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/halo-utility/halo-utility.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/providers/halo-focus/provide-halo-focus.ts","../../../../../libs/yuuvis/client-framework/src/yuuvis-client-framework.ts"],"sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@NgModule({\n imports: [CommonModule],\n})\nexport class YuuvisClientFrameworkModule {}\n","/**\n * List of element selectors that should be excluded from halo focus\n * when they are inside a Material form field component (mat-form-field).\n *\n * These elements typically have their own built-in focus indicators provided\n * by Angular Material, making the halo focus redundant or visually conflicting.\n *\n * **Included Selectors:**\n * - `input` - Text inputs, number inputs, etc. inside mat-form-field\n * - `textarea` - Multi-line text inputs inside mat-form-field\n * - `mat-select` - Material select dropdowns inside mat-form-field\n *\n * **Why Exclude These:**\n * Angular Material form fields provide their own focus styling with floating labels,\n * underlines, and color changes. Adding a halo around these elements would:\n * - Create visual clutter and redundancy\n * - Potentially conflict with Material's focus styling\n * - Reduce the clarity of Material Design's established patterns\n *\n * **Note:** These elements will still receive halo focus if they appear OUTSIDE\n * of mat-form-field containers. The exclusion only applies when they're wrapped\n * in a mat-form-field.\n *\n * @see {@link HaloUtilityService.shouldSkipElementInMatFormField} for implementation\n */\nexport const haloExcludedElementsInMatFormField = ['input', 'textarea', 'mat-select'];\n","/**\n * List of keyboard navigation keys that trigger halo position updates.\n *\n * When any of these keys are pressed while an element is focused, the halo service\n * schedules an immediate position update to ensure the halo follows the focused element\n * if it moves within the page (e.g., scrolling a list, moving through a table).\n *\n * **Included Keys:**\n * - `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown` - Directional navigation\n * - `Home`, `End` - Jump to start/end of content\n * - `PageUp`, `PageDown` - Page-level scrolling\n *\n * **Why This Matters:**\n * Arrow keys and navigation keys can cause the focused element to scroll or move within\n * its container (e.g., scrolling a select dropdown, moving focus in a grid). By tracking\n * these keys, the halo can immediately update its position to follow the element, preventing\n * visual misalignment.\n *\n * **Performance Note:**\n * Updates are scheduled using requestAnimationFrame, so even rapid key presses won't\n * cause performance issues.\n *\n * @see {@link HaloFocusService} for the implementation of keydown handling\n */\nexport const haloFocusNavigationKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'];\n","/**\n * Default offset (in pixels) between the halo border and the focused element's edges.\n *\n * This value determines how far the halo appears from the focused element:\n * - **Positive value** (default: 3): Halo appears outside the element\n * - **Zero (0)**: Halo aligns exactly with element edges (neutral offset)\n * - **Negative value**: Halo appears inside the element (inset)\n *\n * **Override Methods:**\n * - **Per-element**: Use `halo-offset=\"value\"` attribute\n * - **Per-container**: Use `halo-container-offset=\"value\"` or preset attributes\n *\n * @example\n * ```html\n * <!-- Uses default 3px offset (outside) -->\n * <button>Default</button>\n *\n * <!-- Custom 5px offset -->\n * <input halo-offset=\"5\" />\n *\n * ```\n *\n * @default 3\n */\nexport const defaultHaloFocusOffset = 3;\n","/**\n * Default CSS styles for the halo focus element.\n *\n * These styles define the visual appearance and behavior of the halo border that\n * highlights keyboard-focused elements. The halo is a fixed-position overlay that\n * follows the focused element across the page.\n *\n * **Style Properties:**\n * - `position: 'fixed'` - Keeps halo in viewport regardless of scroll position\n * - `pointerEvents: 'none'` - Allows click-through; halo doesn't block interactions\n * - `zIndex: '9999'` - Ensures halo appears above most other elements\n * - `border` - Primary border using CSS variable `--ymt-primary` (2px solid)\n * - `boxShadow` - Soft shadow with 20% opacity for subtle depth effect\n * - `borderRadius` - Default 8px rounded corners for modern appearance\n * - `opacity: '0'` - Initially hidden; visibility controlled by service\n * - `transform: 'translateZ(0)'` - Creates GPU layer for smoother animations\n *\n * **CSS Variable Dependencies:**\n * - `--ymt-primary` - Primary theme color for border and shadow\n *\n * **Note:** These are base styles that can be overridden globally via HaloFocusConfig\n * or per-element using halo-* attributes.\n *\n * @see {@link HaloFocusConfig} for global style customization\n */\nexport const haloFocusStyles: Partial<CSSStyleDeclaration> = {\n position: 'fixed',\n pointerEvents: 'none',\n zIndex: '9999',\n border: '2px solid var(--ymt-primary)',\n boxShadow: '0 0 0 4px rgba(from var(--ymt-primary) r g b / 0.2)',\n borderRadius: '8px',\n opacity: '0',\n transform: 'translateZ(0)'\n};\n","import { inject, Injectable } from '@angular/core';\nimport { HaloFocusConfig } from '../../models';\nimport { HaloUtilityService } from '..';\nimport { haloFocusNavigationKeys } from '../../config';\n\n/**\n * Service that creates and manages a visual \"halo\" border around keyboard-focused elements\n * to enhance navigation visibility and improve accessibility.\n *\n * This service operates globally across the application, automatically tracking focus events\n * and dynamically positioning a halo element around the currently focused element. The halo\n * responds to window scrolls, resizes, and DOM mutations to maintain accurate positioning.\n *\n * **Key Features:**\n * - Keyboard-only focus indication (respects :focus-visible)\n * - Performance-optimized with requestAnimationFrame and runs outside Angular zone\n * - Element-level and container-level customization support\n * - Automatic visibility detection (handles clipping, hidden elements, etc.)\n * - Responsive to dynamic DOM changes via ResizeObserver and MutationObserver\n * - Smart exclusion for Angular Material form fields (prevents redundant styling)\n *\n * **Usage:**\n * 1. Add `provideHaloFocus()` to your providers array in app.config.ts\n * 2. The service initializes automatically and appends a halo element to the DOM\n * 3. Optionally customize appearance globally or per-element using attributes\n *\n * **Element-Level Attributes:**\n * - `halo-skip` - Excludes element from halo highlighting\n * - `halo-color=\"color\"` - Custom halo color (e.g., \"blue\", \"#00ff00\", \"rgb(255,0,0)\")\n * - `halo-offset=\"value\"` - Custom numeric offset in pixels (default: 3)\n *\n * **Container-Level Attributes:**\n * Define default halo styles for all focused children. Child elements can override\n * container settings with their own halo-* attributes.\n *\n * - `halo-container` - Marks element as a halo container (required for other attributes)\n * - `halo-container-skip` - Excludes all focused children from halo highlighting\n * - `halo-container-color=\"color\"` - Default halo color for focused children\n * - `halo-container-offset=\"value\"` - Default offset for focused children\n *\n * **Material Form Field Integration:**\n * Elements inside Angular Material form fields (mat-form-field) are automatically excluded\n * from halo focus to prevent visual conflicts with Material's built-in focus styling.\n * Excluded elements: input, mat-select (configurable via haloExcludedElementsInMatFormField)\n *\n * @example\n * ```html\n * <!-- Element-level customization -->\n * <button halo-color=\"red\">Custom Red Halo</button>\n * <input halo-offset=\"5\" />\n * <select halo-skip>No Halo</select>\n *\n * <!-- Container-level defaults -->\n * <form halo-container halo-container-color=\"blue\" halo-container-offset=\"5\">\n * <input /> <!-- Inherits blue color and 5px offset -->\n * <button halo-color=\"red\">Submit</button> <!-- Overrides with red, keeps 5px offset -->\n * </form>\n * ```\n *\n * **Performance Notes:**\n * - Service runs outside Angular's zone to prevent unnecessary change detection\n * - Uses requestAnimationFrame for smooth rendering\n * - Only shows halo for keyboard interactions (respects :focus-visible)\n *\n * @see {@link provideHaloFocus} for initialization and configuration\n * @see {@link HaloFocusConfig} for global configuration options\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class HaloFocusService {\n //#region Properties\n\n #utility = inject(HaloUtilityService);\n #config: HaloFocusConfig | undefined;\n\n #haloElement: HTMLDivElement | null = null;\n #currentElement: HTMLElement | null = null;\n #lastInteractionWasKeyboard = false;\n\n #rafId: number | null = null; //Request Animation Frame ID\n #resizeObserver?: ResizeObserver;\n #mutationObserver?: MutationObserver;\n\n //#endregion\n\n //#region Init\n\n /**\n * Initializes the Halo Focus service and sets up the global halo tracking system.\n *\n * This method performs the following initialization steps:\n * 1. Applies optional configuration for halo appearance (inner and outer colors)\n * 2. Creates and appends the halo DIV element to the document body\n * 3. Attaches global event listeners for focus, blur, keyboard, mouse, scroll, and resize events\n * 4. Sets up ResizeObserver and MutationObserver for tracking dynamic DOM changes\n *\n * **Lifecycle:**\n * - Called automatically by `provideHaloFocus()` during app initialization\n * - Runs outside Angular's zone to prevent triggering change detection\n * - Initializes only once per application lifecycle\n *\n * **Performance:**\n * - Uses passive event listeners where applicable\n * - Leverages requestAnimationFrame for efficient rendering\n * - Observers are conditionally created only if browser supports them\n *\n * @param config - Optional configuration object for customizing halo appearance\n * @param config.innerColor - Custom inner color (overrides default primary color)\n * @param config.outerColor - Custom outer color (overrides default shadow)\n *\n * @internal This method should not be called manually; use `provideHaloFocus()` instead\n */\n public init(config?: HaloFocusConfig): void {\n this.#initConfig(config);\n this.#appendHaloElementToDOM();\n this.#initListeners();\n this.#initObservers();\n }\n\n #initConfig(config?: HaloFocusConfig): void {\n this.#config = config;\n }\n\n #appendHaloElementToDOM(): void {\n this.#haloElement = document.createElement('div');\n this.#haloElement.id = 'focus-halo';\n this.#haloElement.setAttribute('aria-hidden', 'true');\n Object.assign(this.#haloElement.style, this.#utility.getHaloFocusStyles(this.#config));\n document.body.appendChild(this.#haloElement);\n }\n\n #initListeners(): void {\n document.addEventListener('focus', this.#onFocus, true);\n document.addEventListener('blur', this.#onBlur, true);\n window.addEventListener('scroll', this.#scheduleUpdate, true);\n window.addEventListener('resize', this.#scheduleUpdate);\n document.addEventListener('keydown', this.#onKeydown, true);\n document.addEventListener('mousedown', this.#onMouseDown, true);\n }\n\n #initObservers(): void {\n if ('ResizeObserver' in window) {\n this.#resizeObserver = new ResizeObserver(() => {\n if (this.#currentElement) this.#scheduleUpdate();\n });\n }\n\n if ('MutationObserver' in window) {\n this.#mutationObserver = new MutationObserver(() => {\n if (this.#currentElement) this.#scheduleUpdate();\n });\n }\n }\n\n // #endregion\n\n //#region Rendering\n\n #updateHaloRect = () => {\n if (!this.#currentElement || !this.#haloElement || !document.contains(this.#currentElement)) {\n this.#haloElement && (this.#haloElement.style.opacity = '0');\n this.#currentElement = null;\n return;\n }\n\n // Check if element is actually visible\n if (!this.#utility.isElementVisible(this.#currentElement)) {\n this.#haloElement.style.opacity = '0';\n return;\n }\n\n this.#utility.setHaloElementSize(this.#currentElement, this.#haloElement);\n this.#utility.setCustomColor(this.#currentElement, this.#haloElement, this.#config);\n this.#haloElement.style.opacity = '1';\n };\n\n #scheduleUpdate = () => {\n if (this.#rafId != null) cancelAnimationFrame(this.#rafId);\n this.#rafId = requestAnimationFrame(this.#updateHaloRect);\n };\n\n #startTracking(target: HTMLElement) {\n this.#currentElement = target;\n this.#scheduleUpdate();\n\n // Track size changes on target element\n if (this.#resizeObserver) {\n this.#resizeObserver.observe(target);\n\n // Also observe all parent elements with overflow (they might clip the element)\n let parent = target.parentElement;\n while (parent && parent !== document.body) {\n const style = getComputedStyle(parent);\n const overflow = style.overflow + style.overflowX + style.overflowY;\n if (overflow.includes('hidden') || overflow.includes('clip') || overflow.includes('auto') || overflow.includes('scroll')) {\n this.#resizeObserver.observe(parent);\n }\n parent = parent.parentElement;\n }\n }\n\n // Track style/attribute changes (for position, transform changes)\n if (this.#mutationObserver) {\n this.#mutationObserver.observe(target, {\n attributes: true,\n attributeFilter: ['style', 'class'],\n subtree: false\n });\n }\n }\n\n #stopTracking() {\n if (this.#resizeObserver) this.#resizeObserver.disconnect();\n if (this.#mutationObserver) this.#mutationObserver.disconnect();\n }\n //#endregion\n\n //#region Event handlers\n\n #onKeydown = (e: KeyboardEvent) => {\n this.#lastInteractionWasKeyboard = true;\n // Track arrow keys and other navigation keys that might move focused element\n if (this.#currentElement && haloFocusNavigationKeys.includes(e.key)) {\n this.#scheduleUpdate();\n }\n };\n\n #onMouseDown = () => {\n this.#lastInteractionWasKeyboard = false;\n if (this.#haloElement) {\n this.#haloElement.style.opacity = '0';\n this.#currentElement = null;\n this.#stopTracking();\n }\n };\n\n #onFocus = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n if (!this.#utility.isFocusable(target)) return;\n\n const container = this.#utility.getHaloContainer(target);\n\n // Check all skip conditions\n if (\n target.hasAttribute('halo-skip') ||\n this.#utility.shouldSkipElementInMatFormField(target) ||\n (container && container.hasAttribute('halo-container-skip')) ||\n !this.#utility.matchesFocusVisible(target, this.#lastInteractionWasKeyboard)\n ) {\n this.#haloElement && (this.#haloElement.style.opacity = '0');\n this.#currentElement = null;\n return;\n }\n\n this.#stopTracking();\n this.#startTracking(target);\n };\n\n #onBlur = (e: FocusEvent) => {\n if ((e.target as HTMLElement) === this.#currentElement && this.#haloElement) {\n this.#haloElement.style.opacity = '0';\n this.#currentElement = null;\n this.#stopTracking();\n }\n };\n\n //#endregion\n\n /** Optional: allow manual control from directives/components */\n // public showFor(el: HTMLElement) {\n // this.#currentEl = el;\n // this.#scheduleUpdate();\n // if (this.#ro) {\n // this.#ro.disconnect();\n // this.#ro.observe(el);\n // }\n // }\n\n // public hide() {\n // if (this.#haloEl) this.#haloEl.style.opacity = '0';\n // this.#currentEl = null;\n // if (this.#ro) this.#ro.disconnect();\n // }\n}\n","import { Component, inject, Injectable, signal } from '@angular/core';\nimport { MAT_SNACK_BAR_DATA, MatSnackBar, MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel, MatSnackBarRef } from '@angular/material/snack-bar';\nimport { SnackBarData, SnackBarLevel, SnackBarOptions } from './snack-bar.interface';\nimport { MatButtonModule } from '@angular/material/button';\n@Injectable({\n providedIn: 'root'\n})\nexport class SnackBarService {\n #snackBar = inject(MatSnackBar);\n\n info(message: string, action?: string): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'info' });\n }\n\n success(message: string, action?: string): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'success' });\n }\n\n warning(message: string, action?: string): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'warning' });\n }\n\n danger(message: string, action?: string): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'danger' });\n }\n\n snack(message: string, options: SnackBarOptions): MatSnackBarRef<SnackBarComponent> {\n return this.#snackBar.openFromComponent(SnackBarComponent, {\n data: {\n level: options.level,\n message,\n action: options.action\n },\n duration: options.duration || 3000,\n horizontalPosition: options.horizontalPosition,\n verticalPosition: options.verticalPosition,\n panelClass: ['yuv-snack-bar', 'level-' + options.level]\n });\n }\n}\n\n@Component({\n selector: 'yuv-snack-bar-component',\n template: `\n <span matSnackBarLabel (click)=\"dismiss()\"> {{ message() }} </span>\n @let a = action();\n @if (a) {\n <span matSnackBarActions>\n <button mat-button matSnackBarAction (click)=\"dismiss(!!a)\">{{ action() }}</button>\n </span>\n }\n `,\n styles: `\n :host {\n display: flex;\n }\n `,\n imports: [MatButtonModule, MatSnackBarLabel, MatSnackBarActions, MatSnackBarAction],\n host: {\n '[class.info]': \"level() === 'info'\",\n '[class.success]': \"level() === 'success'\",\n '[class.warning]': \"level() === 'warning'\",\n '[class.danger]': \"level() === 'danger'\"\n }\n})\nexport class SnackBarComponent {\n #snackBarRef = inject(MatSnackBarRef);\n #snackData = inject<SnackBarData>(MAT_SNACK_BAR_DATA);\n level = signal<SnackBarLevel>(this.#snackData.level);\n message = signal<string>(this.#snackData.message);\n action = signal<string | undefined>(this.#snackData.action);\n\n dismiss(withAction = false) {\n if (withAction) {\n this.#snackBarRef.dismissWithAction();\n } else if (!this.action()) {\n this.#snackBarRef.dismiss();\n }\n }\n}\n","import { Injectable } from '@angular/core';\nimport { defaultHaloFocusOffset, haloFocusStyles, haloExcludedElementsInMatFormField } from '../../config';\nimport { HaloFocusConfig } from '../../models';\n\n/**\n * Utility service providing helper methods for the Halo Focus feature.\n *\n * This service contains pure utility functions for:\n * - Element validation (focusability, visibility, focus-visible state)\n * - Material form field detection and exclusion logic\n * - Style calculations (colors, offsets, positioning)\n * - DOM traversal (finding halo containers, checking parent visibility)\n * - Halo element manipulation (size, color, position)\n *\n * **Scope & Performance:**\n * This service is intentionally NOT provided in root (`providedIn: 'root'` is omitted).\n * Instead, it's provided locally via `provideHaloFocus()`, ensuring it's only instantiated\n * when the Halo Focus feature is actually used. This optimization prevents unnecessary\n * service creation in applications that don't use halo focus.\n *\n * **Note:** This service is used exclusively by `HaloFocusService` and should not be\n * injected or used elsewhere in the application.\n *\n * @internal\n */\n@Injectable()\nexport class HaloUtilityService {\n //#region Validations\n\n /**\n * Checks if an element is focusable (can receive keyboard focus).\n *\n * An element is considered focusable if:\n * - It has a non-negative tabIndex (>= 0), OR\n * - It's a natively focusable element (A, BUTTON, INPUT, SELECT, TEXTAREA), OR\n * - It has contentEditable enabled\n *\n * @param element - Element to check\n * @returns true if element can receive focus, false otherwise\n */\n isFocusable(element: Element | null): element is HTMLElement {\n if (!element) return false;\n const h = element as HTMLElement;\n if (h.tabIndex >= 0) return true;\n return /^(A|BUTTON|INPUT|SELECT|TEXTAREA)$/.test(h.tagName) || h.isContentEditable;\n }\n\n /**\n * Checks if an element is inside a Material form field (mat-form-field).\n *\n * Traverses up the DOM tree from the element to check if any parent\n * has the `mat-form-field` tag name. This is used to determine if\n * certain elements should skip halo focus because they're already\n * styled by Angular Material.\n *\n * **Search Scope:**\n * - Starts from element itself\n * - Stops at document.body\n * - Returns true on first mat-form-field match\n *\n * @param element - The element to check\n * @returns true if element is inside a mat-form-field, false otherwise\n */\n isElementInsideMatFormField(element: HTMLElement): boolean {\n let current: HTMLElement | null = element;\n while (current && current !== document.body) {\n if (current.tagName.toLowerCase() === 'mat-form-field') {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n }\n\n /**\n * Determines if an element should be excluded from halo focus when inside mat-form-field.\n *\n * Checks two conditions:\n * 1. Element tag name matches one of the excluded selectors (input, mat-select, etc.)\n * 2. Element is inside a mat-form-field component\n *\n * If both conditions are true, the element should skip halo focus because\n * Angular Material already provides adequate focus styling.\n *\n * **Excluded Elements (configurable via haloExcludedElementsInMatFormField):**\n * - `input` elements inside mat-form-field\n * - `textarea` elements inside mat-form-field\n * - `mat-select` elements inside mat-form-field\n *\n * **Note:** Elements outside mat-form-field will NOT be skipped, even if\n * their tag matches the exclusion list.\n *\n * @param element - The focused element to check\n * @returns true if element should be skipped (no halo), false if halo should be shown\n */\n shouldSkipElementInMatFormField(element: HTMLElement): boolean {\n const tagName = element.tagName.toLowerCase();\n const isExcludedElement = haloExcludedElementsInMatFormField.includes(tagName);\n\n if (!isExcludedElement) {\n return false;\n }\n\n return this.isElementInsideMatFormField(element);\n }\n\n /**\n * Determines if an element should display the halo based on :focus-visible state.\n *\n * This method respects the CSS :focus-visible pseudo-class, which indicates that\n * focus was triggered by keyboard navigation (not mouse clicks). The halo only appears\n * when both conditions are met:\n * 1. Element matches :focus-visible (browser determines this)\n * 2. Last user interaction was via keyboard (tracked by service)\n *\n * **Fallback:** If browser doesn't support :focus-visible, falls back to checking\n * lastInteractionWasKeyboard only.\n *\n * @param element - The currently focused element\n * @param lastInteractionWasKeyboard - Whether the last interaction was a keyboard event\n * @returns true if halo should be visible, false otherwise\n */\n matchesFocusVisible(element: HTMLElement, lastInteractionWasKeyboard: boolean): boolean {\n try {\n const matches = element.matches(':focus-visible');\n if (!matches) return false;\n return matches && lastInteractionWasKeyboard;\n } catch {\n return lastInteractionWasKeyboard;\n }\n }\n\n /**\n * Checks if an element is actually visible on the page.\n *\n * Performs comprehensive visibility checks including:\n * - Element has non-zero dimensions (width and height > 0)\n * - Element is not hidden via CSS (display: none, visibility: hidden, opacity: 0)\n * - Element is not clipped by parent containers with overflow: hidden/clip\n * - All parent elements in the hierarchy are visible\n *\n * This prevents the halo from appearing around technically focused but visually\n * hidden elements (e.g., elements in collapsed sections, off-screen elements).\n *\n * @param element - Element to check for visibility\n * @returns true if element is visible to the user, false otherwise\n */\n isElementVisible(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect();\n\n // Check if element has dimensions\n if (rect.width === 0 || rect.height === 0) {\n return false;\n }\n\n // Check computed style on element itself\n const style = getComputedStyle(element);\n if (style.visibility === 'hidden' || style.display === 'none' || parseFloat(style.opacity) === 0) {\n return false;\n }\n\n // Check if element is clipped by overflow:hidden parent OR if any parent is hidden\n return this.isParentVisible(element, rect);\n }\n\n /**\n * Recursively checks if all parent elements are visible and not clipping the target element.\n *\n * Traverses up the DOM tree from the element to document.body, checking each parent for:\n * - Hidden state (display: none, visibility: hidden, opacity: 0)\n * - Overflow clipping (overflow: hidden/clip) that completely hides the element\n *\n * If any parent has overflow clipping, calculates the visible intersection area.\n * Returns false if element is completely clipped (no visible area).\n *\n * @param element - The element whose parents should be checked\n * @param rect - The bounding rectangle of the element\n * @returns true if element has visible area within all parent containers, false otherwise\n */\n isParentVisible(element: HTMLElement, rect: DOMRect): boolean {\n let parent = element.parentElement;\n while (parent && parent !== document.body) {\n const parentStyle = getComputedStyle(parent);\n\n // Check if parent is hidden\n if (parentStyle.display === 'none' || parentStyle.visibility === 'hidden' || parseFloat(parentStyle.opacity) === 0) {\n return false;\n }\n\n const parentOverflow = parentStyle.overflow + parentStyle.overflowX + parentStyle.overflowY;\n\n if (parentOverflow.includes('hidden') || parentOverflow.includes('clip')) {\n const parentRect = parent.getBoundingClientRect();\n // Calculate intersection - if there's no visible area, element is hidden\n const visibleRight = Math.min(rect.right, parentRect.right);\n const visibleLeft = Math.max(rect.left, parentRect.left);\n const visibleBottom = Math.min(rect.bottom, parentRect.bottom);\n const visibleTop = Math.max(rect.top, parentRect.top);\n\n const visibleWidth = visibleRight - visibleLeft;\n const visibleHeight = visibleBottom - visibleTop;\n\n // If no intersection or intersection is too small (less than 1px), element is not visible\n if (visibleWidth <= 0 || visibleHeight <= 0) {\n return false;\n }\n }\n parent = parent.parentElement;\n }\n return true;\n }\n\n //#endregion\n\n // #region Utilities\n\n /**\n * Generates the complete CSS styles for the halo element, merging defaults with custom config.\n *\n * Takes the base styles from `haloFocusStyles` constant and overrides specific properties\n * if custom configuration is provided. This allows global style customization while\n * maintaining all required base styles.\n *\n * **Configurable Properties:**\n * - Inner color (via config.innerColor)\n * - Outer color (via config.outerColor)\n *\n * **Non-configurable Properties:**\n * - position, pointerEvents, zIndex, opacity, transform (always use defaults)\n *\n * @param config - Optional global configuration object\n * @returns Complete CSS style declaration object for the halo element\n */\n getHaloFocusStyles(config?: HaloFocusConfig): Partial<CSSStyleDeclaration> {\n return {\n ...haloFocusStyles,\n border: config?.innerColor ? `2px solid ${config.innerColor}` : haloFocusStyles.border,\n boxShadow: config?.outerColor ? `0 0 0 4px ${config.outerColor}` : haloFocusStyles.boxShadow\n };\n }\n\n /**\n * Finds the nearest parent element marked as a halo container.\n *\n * Traverses up the DOM tree looking for an element with the `halo-container` attribute.\n * Container elements can define default halo styles (color, offset) that apply to all\n * their focused children, unless overridden by child-specific halo-* attributes.\n *\n * **Search Scope:**\n * - Starts from element's immediate parent\n * - Stops at document.body (does not check body itself)\n * - Returns first matching ancestor\n *\n * @param element - The focused element to search from\n * @returns The nearest halo container element, or null if none found\n */\n getHaloContainer(element: HTMLElement): HTMLElement | null {\n let parent = element.parentElement;\n while (parent && parent !== document.body) {\n if (parent.hasAttribute('halo-container')) {\n return parent;\n }\n parent = parent.parentElement;\n }\n return null;\n }\n\n /**\n * Converts any CSS color format to rgba with specified alpha transparency.\n *\n * Handles multiple color formats:\n * - **rgba**: Replaces existing alpha value\n * - **rgb**: Converts to rgba by appending alpha\n * - **Hex** (#RRGGBB): Converts to rgba format\n * - **Named colors**: Uses modern CSS `color-mix` function with alpha\n *\n * Used internally to create outer colors with reduced opacity from inner colors.\n *\n * @param color - CSS color in any valid format (rgba, rgb, hex, named)\n * @param alpha - Alpha transparency value (0-1)\n * @returns Color in rgba format with specified alpha\n */\n getColorWithAlpha(color: string, alpha: number): string {\n // If already rgba, try to replace alpha\n if (color.startsWith('rgba')) {\n return color.replace(/[\\d.]+\\)$/, `${alpha})`);\n }\n\n // If rgb, convert to rgba\n if (color.startsWith('rgb')) {\n return color.replace('rgb', 'rgba').replace(')', `, ${alpha})`);\n }\n\n // If hex color, convert to rgba\n if (color.startsWith('#')) {\n const hex = color.replace('#', '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n\n // For named colors or other formats, wrap in rgba with color-mix (modern approach)\n // Fallback to appending alpha as hex if browser doesn't support color-mix\n return `rgba(from ${color} r g b / ${alpha})`;\n }\n\n /**\n * Calculates the offset (distance) between the halo border and element edges.\n *\n * Determines offset using the following priority order:\n * 1. **Element's own attributes** (highest priority):\n * - `halo-offset=\"value\"` - Custom numeric value\n * 2. **Parent container attributes** (if element has no offset):\n * - `halo-container-offset=\"value\"`\n * 3. **Default offset** (if nothing specified): 3px\n *\n * @param element - The focused element to calculate offset for\n * @returns Offset value in pixels\n */\n getTargetOffset(element: HTMLElement | null): number {\n if (!element) return defaultHaloFocusOffset;\n\n // 1. Check element's own attributes first\n const customOffset = element.getAttribute('halo-offset');\n if (customOffset !== null) {\n const parsed = parseFloat(customOffset);\n if (!isNaN(parsed)) return parsed;\n }\n\n // 2. Check container attributes as fallback\n const container = this.getHaloContainer(element);\n if (container) {\n const containerOffset = container.getAttribute('halo-container-offset');\n if (containerOffset !== null) {\n const parsed = parseFloat(containerOffset);\n if (!isNaN(parsed)) return parsed;\n }\n }\n\n // 3. Default: positive offset (halo outside element)\n return defaultHaloFocusOffset;\n }\n\n /**\n * Applies custom color styling to the halo element based on element or container attributes.\n *\n * Color resolution priority:\n * 1. **Element's own color**: `halo-color=\"color\"` attribute (highest priority)\n * 2. **Container's color**: `halo-container-color=\"color\"` from nearest halo container\n * 3. **Default color**: From global config or base styles (if no custom color found)\n *\n * When custom color is found:\n * - Sets inner color (2px solid border) with the custom color\n * - Sets outer color (shadow) to match, with 20% alpha transparency for subtle depth\n *\n * When no custom color:\n * - Resets to default styles from config or haloFocusStyles constant\n *\n * @param element - The focused element (checked for halo-color attribute)\n * @param haloElement - The halo DIV element to apply styles to\n * @param config - Optional global config for default colors\n */\n setCustomColor(element: HTMLElement, haloElement: HTMLDivElement, config?: HaloFocusConfig): void {\n // 1. Check element's own color first\n let customColor = element.getAttribute('halo-color');\n\n // 2. If not found, check container color\n if (!customColor) {\n const container = this.getHaloContainer(element);\n if (container) {\n customColor = container.getAttribute('halo-container-color');\n }\n }\n // Apply color or reset to default\n if (customColor) {\n haloElement.style.border = `2px solid ${customColor}`;\n haloElement.style.boxShadow = `0 0 0 4px ${this.getColorWithAlpha(customColor, 0.2)}`;\n } else {\n // Reset to default styles\n haloElement.style.border = this.getHaloFocusStyles(config).border!;\n haloElement.style.boxShadow = this.getHaloFocusStyles(config).boxShadow!;\n }\n }\n\n /**\n * Positions and sizes the halo element to surround the focused element.\n *\n * Calculates halo dimensions and position based on:\n * - Element's bounding rectangle (viewport-relative position)\n * - Calculated offset from getTargetOffset() (custom or default)\n * - Element's border-radius for matching rounded corners\n *\n * **Calculation Details:**\n * - Position: Element's top/left minus offset (for outside positioning)\n * - Size: Element's width/height plus 2× offset (to extend on all sides)\n * - Border radius: Inherited from focused element's computed style\n *\n * Uses Math.floor for position and Math.ceil for dimensions to prevent\n * subpixel rendering issues and ensure full element coverage.\n *\n * @param currentElement - The currently focused element to surround\n * @param haloElement - The halo DIV element to position and size\n */\n setHaloElementSize(currentElement: HTMLElement, haloElement: HTMLDivElement): void {\n const rect = currentElement.getBoundingClientRect();\n const offset = this.getTargetOffset(currentElement);\n\n const left = Math.floor(rect.left - offset);\n const top = Math.floor(rect.top - offset);\n const width = Math.ceil(rect.width + offset * 2);\n const height = Math.ceil(rect.height + offset * 2);\n\n const cs = getComputedStyle(currentElement);\n haloElement.style.left = `${left}px`;\n haloElement.style.top = `${top}px`;\n haloElement.style.width = `${width}px`;\n haloElement.style.height = `${height}px`;\n haloElement.style.borderRadius = cs.borderRadius || '8px';\n }\n\n //#endregion\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders, NgZone, provideAppInitializer } from '@angular/core';\nimport { HaloFocusService, HaloUtilityService } from '../../services';\nimport { HaloFocusConfig } from '../../models';\n\n/**\n * Provides and initializes the Halo Focus feature for the Angular application.\n *\n * This function sets up a global visual accessibility enhancement that creates a\n * \"halo\" border around keyboard-focused elements, making navigation more visible\n * and improving the user experience for keyboard users.\n *\n * **What It Does:**\n * - Registers HaloFocusService and HaloUtility as application-wide providers\n * - Automatically initializes the service during app startup via APP_INITIALIZER\n * - Runs initialization outside Angular zone for optimal performance\n * - Creates a persistent halo element that tracks focus throughout the application\n *\n * **Key Features:**\n * - Keyboard-only focus indication (respects CSS :focus-visible)\n * - Performance-optimized with requestAnimationFrame and zone-free execution\n * - Fully customizable via global config or per-element attributes\n * - Supports container-level defaults for consistent styling\n * - Automatic visibility detection and responsive to DOM changes\n *\n * **Configuration Options:**\n * @param config - Optional global configuration object\n * @param config.innerColor - Inner color of the halo (default: 'var(--ymt-primary)')\n * @param config.outerColor - Outer color around the halo (default: 'rgba(from var(--ymt-primary) r g b / 0.2)')\n *\n * @returns EnvironmentProviders for the Halo Focus feature\n *\n * @example\n * // Basic usage in app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHaloFocus()\n * ]\n * };\n *\n * @example\n * // With custom configuration\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHaloFocus({\n * innerColor: '#007bff',\n * outerColor: 'rgba(0, 123, 255, 0.25)'\n * })\n * ]\n * };\n *\n * @example\n * // Using element-level attributes in your template\n * <button halo-color=\"red\">Custom Red Halo</button>\n * <input halo-offset=\"5\" />\n * <select halo-skip>No Halo</select>\n *\n * @example\n * // Using container-level attributes\n * <form halo-container halo-container-color=\"blue\" halo-container-offset=\"5\">\n * <input /> <!-- Will use blue color and 5px offset -->\n * <button halo-color=\"red\">Submit</button> <!-- Overrides with red -->\n * </form>\n */\nexport function provideHaloFocus(config?: HaloFocusConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n HaloUtilityService,\n provideAppInitializer(() => {\n const zone = inject(NgZone);\n const haloFocus = inject(HaloFocusService);\n zone.runOutsideAngular(() => haloFocus.init(config));\n })\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"yuuvis-client-framework.mjs","sources":["../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-excluded-elements.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-focus-navigation-keys.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-focus-offset.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/halo-focus-defaults/halo-focus-styles.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/session/session-default-duration.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/session/session-activity-window-before-end.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/config/session/session-popup-before-end.const.ts","../../../../../libs/yuuvis/client-framework/src/lib/enums/channel-message.enum.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/halo-focus/halo-focus.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/halo-utility/halo-utility.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/snack-bar/snack-bar.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/services/session/session.service.ts","../../../../../libs/yuuvis/client-framework/src/lib/providers/halo-focus/provide-halo-focus.ts","../../../../../libs/yuuvis/client-framework/src/lib/providers/session/provide-session.provider.ts","../../../../../libs/yuuvis/client-framework/src/lib/yuuvis-client-framework.module.ts","../../../../../libs/yuuvis/client-framework/src/yuuvis-client-framework.ts"],"sourcesContent":["/**\n * List of element selectors that should be excluded from halo focus\n * when they are inside a Material form field component (mat-form-field).\n *\n * These elements typically have their own built-in focus indicators provided\n * by Angular Material, making the halo focus redundant or visually conflicting.\n *\n * **Included Selectors:**\n * - `input` - Text inputs, number inputs, etc. inside mat-form-field\n * - `textarea` - Multi-line text inputs inside mat-form-field\n * - `mat-select` - Material select dropdowns inside mat-form-field\n *\n * **Why Exclude These:**\n * Angular Material form fields provide their own focus styling with floating labels,\n * underlines, and color changes. Adding a halo around these elements would:\n * - Create visual clutter and redundancy\n * - Potentially conflict with Material's focus styling\n * - Reduce the clarity of Material Design's established patterns\n *\n * **Note:** These elements will still receive halo focus if they appear OUTSIDE\n * of mat-form-field containers. The exclusion only applies when they're wrapped\n * in a mat-form-field.\n *\n * @see {@link HaloUtilityService.shouldSkipElementInMatFormField} for implementation\n */\nexport const haloExcludedElementsInMatFormField = ['input', 'textarea', 'mat-select'];\n","/**\n * List of keyboard navigation keys that trigger halo position updates.\n *\n * When any of these keys are pressed while an element is focused, the halo service\n * schedules an immediate position update to ensure the halo follows the focused element\n * if it moves within the page (e.g., scrolling a list, moving through a table).\n *\n * **Included Keys:**\n * - `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown` - Directional navigation\n * - `Home`, `End` - Jump to start/end of content\n * - `PageUp`, `PageDown` - Page-level scrolling\n *\n * **Why This Matters:**\n * Arrow keys and navigation keys can cause the focused element to scroll or move within\n * its container (e.g., scrolling a select dropdown, moving focus in a grid). By tracking\n * these keys, the halo can immediately update its position to follow the element, preventing\n * visual misalignment.\n *\n * **Performance Note:**\n * Updates are scheduled using requestAnimationFrame, so even rapid key presses won't\n * cause performance issues.\n *\n * @see {@link HaloFocusService} for the implementation of keydown handling\n */\nexport const haloFocusNavigationKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'];\n","/**\n * Default offset (in pixels) between the halo border and the focused element's edges.\n *\n * This value determines how far the halo appears from the focused element:\n * - **Positive value** (default: 3): Halo appears outside the element\n * - **Zero (0)**: Halo aligns exactly with element edges (neutral offset)\n * - **Negative value**: Halo appears inside the element (inset)\n *\n * **Override Methods:**\n * - **Per-element**: Use `halo-offset=\"value\"` attribute\n * - **Per-container**: Use `halo-container-offset=\"value\"` or preset attributes\n *\n * @example\n * ```html\n * <!-- Uses default 3px offset (outside) -->\n * <button>Default</button>\n *\n * <!-- Custom 5px offset -->\n * <input halo-offset=\"5\" />\n *\n * ```\n *\n * @default 3\n */\nexport const defaultHaloFocusOffset = 3;\n","/**\n * Default CSS styles for the halo focus element.\n *\n * These styles define the visual appearance and behavior of the halo border that\n * highlights keyboard-focused elements. The halo is a fixed-position overlay that\n * follows the focused element across the page.\n *\n * **Style Properties:**\n * - `position: 'fixed'` - Keeps halo in viewport regardless of scroll position\n * - `pointerEvents: 'none'` - Allows click-through; halo doesn't block interactions\n * - `zIndex: '9999'` - Ensures halo appears above most other elements\n * - `border` - Primary border using CSS variable `--ymt-primary` (2px solid)\n * - `boxShadow` - Soft shadow with 20% opacity for subtle depth effect\n * - `borderRadius` - Default 8px rounded corners for modern appearance\n * - `opacity: '0'` - Initially hidden; visibility controlled by service\n * - `transform: 'translateZ(0)'` - Creates GPU layer for smoother animations\n *\n * **CSS Variable Dependencies:**\n * - `--ymt-primary` - Primary theme color for border and shadow\n *\n * **Note:** These are base styles that can be overridden globally via HaloFocusConfig\n * or per-element using halo-* attributes.\n *\n * @see {@link HaloFocusConfig} for global style customization\n */\nexport const haloFocusStyles: Partial<CSSStyleDeclaration> = {\n position: 'fixed',\n pointerEvents: 'none',\n zIndex: '9999',\n border: '2px solid var(--ymt-primary)',\n boxShadow: '0 0 0 4px rgba(from var(--ymt-primary) r g b / 0.2)',\n borderRadius: '8px',\n opacity: '0',\n transform: 'translateZ(0)'\n};\n","/**\n * Default session duration (in milliseconds) when no explicit duration is provided.\n *\n * This value is used as the fallback session lifetime when:\n * - `provideSession()` is called without a duration parameter\n * - Before `startSession(duration)` is called with a backend-provided value\n *\n * **Timeline:**\n * ```\n * [Session Start] ──────────────────────────────► [Session Expires]\n * ← sessionDefaultDuration →\n * ```\n *\n * **Override Methods:**\n * - **At startup (known duration)**: `provideSession(customDuration)`\n * - **After login (dynamic duration)**: `sessionService.startSession(backendDuration)`\n *\n * **Usage Scenarios:**\n *\n * Scenario 1 - Fixed duration:\n * ```ts\n * // app.config.ts\n * providers: [\n * provideSession(45 * 60 * 1000) // Override to 45 minutes\n * ]\n * ```\n *\n * Scenario 2 - Backend-provided duration:\n * ```ts\n * // app.config.ts\n * providers: [\n * provideSession() // Uses default 30 minutes initially\n * ]\n *\n * // auth.service.ts (after login)\n * login().subscribe(response => {\n * sessionService.startSession(response.sessionExpiresIn); // Update to backend value\n * });\n * ```\n *\n * **Related Constants:**\n * - {@link sessionActivityWindowBeforeEnd} - When to start tracking user activity\n * - {@link sessionPopupBeforeEnd} - When to show the expiry warning popup\n *\n * @default 30 minutes (1800000 milliseconds)\n */\nexport const sessionDefaultDuration = 30 * 60 * 1000; // 30 minutes","/**\n * Time window (in milliseconds) before session expiry when user activity triggers auto-extension.\n *\n * During this window, the service tracks user interactions (mouse, keyboard, scroll) and HTTP activity.\n * If any activity is detected, the session is automatically extended, preventing the expiry warning popup.\n *\n * **Session Timeline:**\n * ```\n * [Session Start] ──────────────────────────────► [Session Expires]\n * ▲ ▲\n * │ └─ sessionPopupBeforeEnd (1 min)\n * └─ Activity window starts (2 min)\n *\n * ┌─────────────────────────────────────────────┐\n * │ Activity Window (2 minutes) │\n * │ • Tracks: mousemove, keydown, click, scroll │\n * │ • Tracks: HTTP requests (debounced) │\n * │ • If activity detected → auto-extend │\n * │ • If no activity → show warning popup │\n * └─────────────────────────────────────────────┘\n * ```\n *\n * **Why This Matters:**\n * - Prevents unnecessary interruptions for active users\n * - Reduces server-side session renewal traffic by batching extensions\n * - Provides a grace period before showing the expiry warning\n *\n * **Tracked Activities:**\n * - **User interactions**: `mousemove`, `keydown`, `click`, `scroll` on `window`\n * - **HTTP traffic**: Any request via `BackendService.httpCommunicationOccurred$` (debounced 500ms)\n *\n * **Interaction with Other Constants:**\n * - Must be **greater than** {@link sessionPopupBeforeEnd} to allow the activity window before the popup\n * - Typical relationship: `activityWindow > popupWarning` (e.g., 2 min > 1 min)\n *\n * **Performance Note:**\n * Activity tracking is only active during this window, not throughout the entire session,\n * minimizing performance impact of event listeners.\n *\n * @default 2 minutes (120000 milliseconds)\n * @see {@link SessionService.setupUserActivityTracking} for implementation details\n */\nexport const sessionActivityWindowBeforeEnd = 2 * 60 * 1000; // 2 minutes","/**\n * Time (in milliseconds) before session expiry when the warning popup is displayed.\n *\n * When this threshold is reached without detected user activity during the activity window,\n * a snackbar notification appears, warning the user that their session will expire soon\n * and providing an \"Extend session\" action button.\n *\n * **Session Timeline:**\n * ```\n * [Session Start] ──────────────────────────────► [Session Expires]\n * ▲ ▲\n * │ └─ Popup appears (1 min before expiry)\n * └─ Activity window starts (2 min before expiry)\n *\n * User has no activity during the 2-minute window:\n * ├─ 2 min before: Activity tracking starts\n * ├─ 1 min before: ⚠️ Popup shows \"Session expires in one minute\"\n * └─ 0 min: Auto-logout if no action taken\n * ```\n *\n * **Popup Behavior:**\n * - Displays a warning message: \"Session expires in one minute\"\n * - Provides an action button: \"Extend session\"\n * - Clicking \"Extend session\" → extends session by the full session duration\n * - Ignoring the popup → automatic logout when timer reaches zero\n * - Only one popup is shown at a time (subsequent extensions dismiss the existing popup)\n *\n * **Cross-Tab Synchronization:**\n * If the user extends the session in one browser tab, all other tabs:\n * - Automatically dismiss their popups (if shown)\n * - Reset their timers to the new expiry time\n * - Avoid showing redundant warnings\n *\n * **Interaction with Other Constants:**\n * - Must be **less than** {@link sessionActivityWindowBeforeEnd}\n * - Typical relationship: `activityWindow > popupWarning` (e.g., 2 min > 1 min)\n * - The gap between them determines how long the activity window runs before the popup\n *\n * **UX Considerations:**\n * - **1 minute** gives users enough time to react without being too intrusive\n * - Too short (e.g., 10 seconds): Users may not have time to respond\n * - Too long (e.g., 5 minutes): Users may be unnecessarily interrupted if they return\n *\n * @default 1 minute (60000 milliseconds)\n * @see {@link SessionService.showPopup} for popup implementation\n */\nexport const sessionPopupBeforeEnd = 60 * 1000; // 1 minute","export enum ChannelMessage {\n SessionExtended = 'SessionExtended',\n SessionLogout = 'SessionLogout'\n}\n","import { inject, Injectable } from '@angular/core';\nimport { HaloFocusConfig } from '../../models';\nimport { HaloUtilityService } from '..';\nimport { haloFocusNavigationKeys } from '../../config';\n\n/**\n * Service that creates and manages a visual \"halo\" border around keyboard-focused elements\n * to enhance navigation visibility and improve accessibility.\n *\n * This service operates globally across the application, automatically tracking focus events\n * and dynamically positioning a halo element around the currently focused element. The halo\n * responds to window scrolls, resizes, and DOM mutations to maintain accurate positioning.\n *\n * **Key Features:**\n * - Keyboard-only focus indication (respects :focus-visible)\n * - Performance-optimized with requestAnimationFrame and runs outside Angular zone\n * - Element-level and container-level customization support\n * - Automatic visibility detection (handles clipping, hidden elements, etc.)\n * - Responsive to dynamic DOM changes via ResizeObserver and MutationObserver\n * - Smart exclusion for Angular Material form fields (prevents redundant styling)\n *\n * **Usage:**\n * 1. Add `provideHaloFocus()` to your providers array in app.config.ts\n * 2. The service initializes automatically and appends a halo element to the DOM\n * 3. Optionally customize appearance globally or per-element using attributes\n *\n * **Element-Level Attributes:**\n * - `halo-skip` - Excludes element from halo highlighting\n * - `halo-color=\"color\"` - Custom halo color (e.g., \"blue\", \"#00ff00\", \"rgb(255,0,0)\")\n * - `halo-offset=\"value\"` - Custom numeric offset in pixels (default: 3)\n *\n * **Container-Level Attributes:**\n * Define default halo styles for all focused children. Child elements can override\n * container settings with their own halo-* attributes.\n *\n * - `halo-container` - Marks element as a halo container (required for other attributes)\n * - `halo-container-skip` - Excludes all focused children from halo highlighting\n * - `halo-container-color=\"color\"` - Default halo color for focused children\n * - `halo-container-offset=\"value\"` - Default offset for focused children\n *\n * **Material Form Field Integration:**\n * Elements inside Angular Material form fields (mat-form-field) are automatically excluded\n * from halo focus to prevent visual conflicts with Material's built-in focus styling.\n * Excluded elements: input, mat-select (configurable via haloExcludedElementsInMatFormField)\n *\n * @example\n * ```html\n * <!-- Element-level customization -->\n * <button halo-color=\"red\">Custom Red Halo</button>\n * <input halo-offset=\"5\" />\n * <select halo-skip>No Halo</select>\n *\n * <!-- Container-level defaults -->\n * <form halo-container halo-container-color=\"blue\" halo-container-offset=\"5\">\n * <input /> <!-- Inherits blue color and 5px offset -->\n * <button halo-color=\"red\">Submit</button> <!-- Overrides with red, keeps 5px offset -->\n * </form>\n * ```\n *\n * **Performance Notes:**\n * - Service runs outside Angular's zone to prevent unnecessary change detection\n * - Uses requestAnimationFrame for smooth rendering\n * - Only shows halo for keyboard interactions (respects :focus-visible)\n *\n * @see {@link provideHaloFocus} for initialization and configuration\n * @see {@link HaloFocusConfig} for global configuration options\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class HaloFocusService {\n //#region Properties\n\n #utility = inject(HaloUtilityService);\n #config: HaloFocusConfig | undefined;\n\n #haloElement: HTMLDivElement | null = null;\n #currentElement: HTMLElement | null = null;\n #lastInteractionWasKeyboard = false;\n\n #rafId: number | null = null; //Request Animation Frame ID\n #resizeObserver?: ResizeObserver;\n #mutationObserver?: MutationObserver;\n\n //#endregion\n\n //#region Init\n\n /**\n * Initializes the Halo Focus service and sets up the global halo tracking system.\n *\n * This method performs the following initialization steps:\n * 1. Applies optional configuration for halo appearance (inner and outer colors)\n * 2. Creates and appends the halo DIV element to the document body\n * 3. Attaches global event listeners for focus, blur, keyboard, mouse, scroll, and resize events\n * 4. Sets up ResizeObserver and MutationObserver for tracking dynamic DOM changes\n *\n * **Lifecycle:**\n * - Called automatically by `provideHaloFocus()` during app initialization\n * - Runs outside Angular's zone to prevent triggering change detection\n * - Initializes only once per application lifecycle\n *\n * **Performance:**\n * - Uses passive event listeners where applicable\n * - Leverages requestAnimationFrame for efficient rendering\n * - Observers are conditionally created only if browser supports them\n *\n * @param config - Optional configuration object for customizing halo appearance\n * @param config.innerColor - Custom inner color (overrides default primary color)\n * @param config.outerColor - Custom outer color (overrides default shadow)\n *\n * @internal This method should not be called manually; use `provideHaloFocus()` instead\n */\n public init(config?: HaloFocusConfig): void {\n this.#initConfig(config);\n this.#appendHaloElementToDOM();\n this.#initListeners();\n this.#initObservers();\n }\n\n #initConfig(config?: HaloFocusConfig): void {\n this.#config = config;\n }\n\n #appendHaloElementToDOM(): void {\n this.#haloElement = document.createElement('div');\n this.#haloElement.id = 'focus-halo';\n this.#haloElement.setAttribute('aria-hidden', 'true');\n Object.assign(this.#haloElement.style, this.#utility.getHaloFocusStyles(this.#config));\n document.body.appendChild(this.#haloElement);\n }\n\n #initListeners(): void {\n document.addEventListener('focus', this.#onFocus, true);\n document.addEventListener('blur', this.#onBlur, true);\n window.addEventListener('scroll', this.#scheduleUpdate, true);\n window.addEventListener('resize', this.#scheduleUpdate);\n document.addEventListener('keydown', this.#onKeydown, true);\n document.addEventListener('mousedown', this.#onMouseDown, true);\n }\n\n #initObservers(): void {\n if ('ResizeObserver' in window) {\n this.#resizeObserver = new ResizeObserver(() => {\n if (this.#currentElement) this.#scheduleUpdate();\n });\n }\n\n if ('MutationObserver' in window) {\n this.#mutationObserver = new MutationObserver(() => {\n if (this.#currentElement) this.#scheduleUpdate();\n });\n }\n }\n\n // #endregion\n\n //#region Rendering\n\n #updateHaloRect = () => {\n if (!this.#currentElement || !this.#haloElement || !document.contains(this.#currentElement)) {\n this.#haloElement && (this.#haloElement.style.opacity = '0');\n this.#currentElement = null;\n return;\n }\n\n // Check if element is actually visible\n if (!this.#utility.isElementVisible(this.#currentElement)) {\n this.#haloElement.style.opacity = '0';\n return;\n }\n\n this.#utility.setHaloElementSize(this.#currentElement, this.#haloElement);\n this.#utility.setCustomColor(this.#currentElement, this.#haloElement, this.#config);\n this.#haloElement.style.opacity = '1';\n };\n\n #scheduleUpdate = () => {\n if (this.#rafId != null) cancelAnimationFrame(this.#rafId);\n this.#rafId = requestAnimationFrame(this.#updateHaloRect);\n };\n\n #startTracking(target: HTMLElement) {\n this.#currentElement = target;\n this.#scheduleUpdate();\n\n // Track size changes on target element\n if (this.#resizeObserver) {\n this.#resizeObserver.observe(target);\n\n // Also observe all parent elements with overflow (they might clip the element)\n let parent = target.parentElement;\n while (parent && parent !== document.body) {\n const style = getComputedStyle(parent);\n const overflow = style.overflow + style.overflowX + style.overflowY;\n if (overflow.includes('hidden') || overflow.includes('clip') || overflow.includes('auto') || overflow.includes('scroll')) {\n this.#resizeObserver.observe(parent);\n }\n parent = parent.parentElement;\n }\n }\n\n // Track style/attribute changes (for position, transform changes)\n if (this.#mutationObserver) {\n this.#mutationObserver.observe(target, {\n attributes: true,\n attributeFilter: ['style', 'class'],\n subtree: false\n });\n }\n }\n\n #stopTracking() {\n if (this.#resizeObserver) this.#resizeObserver.disconnect();\n if (this.#mutationObserver) this.#mutationObserver.disconnect();\n }\n //#endregion\n\n //#region Event handlers\n\n #onKeydown = (e: KeyboardEvent) => {\n this.#lastInteractionWasKeyboard = true;\n // Track arrow keys and other navigation keys that might move focused element\n if (this.#currentElement && haloFocusNavigationKeys.includes(e.key)) {\n this.#scheduleUpdate();\n }\n };\n\n #onMouseDown = () => {\n this.#lastInteractionWasKeyboard = false;\n if (this.#haloElement) {\n this.#haloElement.style.opacity = '0';\n this.#currentElement = null;\n this.#stopTracking();\n }\n };\n\n #onFocus = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n if (!this.#utility.isFocusable(target)) return;\n\n const container = this.#utility.getHaloContainer(target);\n\n // Check all skip conditions\n if (\n target.hasAttribute('halo-skip') ||\n this.#utility.shouldSkipElementInMatFormField(target) ||\n (container && container.hasAttribute('halo-container-skip')) ||\n !this.#utility.matchesFocusVisible(target, this.#lastInteractionWasKeyboard)\n ) {\n this.#haloElement && (this.#haloElement.style.opacity = '0');\n this.#currentElement = null;\n return;\n }\n\n this.#stopTracking();\n this.#startTracking(target);\n };\n\n #onBlur = (e: FocusEvent) => {\n if ((e.target as HTMLElement) === this.#currentElement && this.#haloElement) {\n this.#haloElement.style.opacity = '0';\n this.#currentElement = null;\n this.#stopTracking();\n }\n };\n\n //#endregion\n\n /** Optional: allow manual control from directives/components */\n // public showFor(el: HTMLElement) {\n // this.#currentEl = el;\n // this.#scheduleUpdate();\n // if (this.#ro) {\n // this.#ro.disconnect();\n // this.#ro.observe(el);\n // }\n // }\n\n // public hide() {\n // if (this.#haloEl) this.#haloEl.style.opacity = '0';\n // this.#currentEl = null;\n // if (this.#ro) this.#ro.disconnect();\n // }\n}\n","import { Injectable } from '@angular/core';\nimport { defaultHaloFocusOffset, haloFocusStyles, haloExcludedElementsInMatFormField } from '../../config';\nimport { HaloFocusConfig } from '../../models';\n\n/**\n * Utility service providing helper methods for the Halo Focus feature.\n *\n * This service contains pure utility functions for:\n * - Element validation (focusability, visibility, focus-visible state)\n * - Material form field detection and exclusion logic\n * - Style calculations (colors, offsets, positioning)\n * - DOM traversal (finding halo containers, checking parent visibility)\n * - Halo element manipulation (size, color, position)\n *\n * **Scope & Performance:**\n * This service is intentionally NOT provided in root (`providedIn: 'root'` is omitted).\n * Instead, it's provided locally via `provideHaloFocus()`, ensuring it's only instantiated\n * when the Halo Focus feature is actually used. This optimization prevents unnecessary\n * service creation in applications that don't use halo focus.\n *\n * **Note:** This service is used exclusively by `HaloFocusService` and should not be\n * injected or used elsewhere in the application.\n *\n * @internal\n */\n@Injectable()\nexport class HaloUtilityService {\n //#region Validations\n\n /**\n * Checks if an element is focusable (can receive keyboard focus).\n *\n * An element is considered focusable if:\n * - It has a non-negative tabIndex (>= 0), OR\n * - It's a natively focusable element (A, BUTTON, INPUT, SELECT, TEXTAREA), OR\n * - It has contentEditable enabled\n *\n * @param element - Element to check\n * @returns true if element can receive focus, false otherwise\n */\n isFocusable(element: Element | null): element is HTMLElement {\n if (!element) return false;\n const h = element as HTMLElement;\n if (h.tabIndex >= 0) return true;\n return /^(A|BUTTON|INPUT|SELECT|TEXTAREA)$/.test(h.tagName) || h.isContentEditable;\n }\n\n /**\n * Checks if an element is inside a Material form field (mat-form-field).\n *\n * Traverses up the DOM tree from the element to check if any parent\n * has the `mat-form-field` tag name. This is used to determine if\n * certain elements should skip halo focus because they're already\n * styled by Angular Material.\n *\n * **Search Scope:**\n * - Starts from element itself\n * - Stops at document.body\n * - Returns true on first mat-form-field match\n *\n * @param element - The element to check\n * @returns true if element is inside a mat-form-field, false otherwise\n */\n isElementInsideMatFormField(element: HTMLElement): boolean {\n let current: HTMLElement | null = element;\n while (current && current !== document.body) {\n if (current.tagName.toLowerCase() === 'mat-form-field') {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n }\n\n /**\n * Determines if an element should be excluded from halo focus when inside mat-form-field.\n *\n * Checks two conditions:\n * 1. Element tag name matches one of the excluded selectors (input, mat-select, etc.)\n * 2. Element is inside a mat-form-field component\n *\n * If both conditions are true, the element should skip halo focus because\n * Angular Material already provides adequate focus styling.\n *\n * **Excluded Elements (configurable via haloExcludedElementsInMatFormField):**\n * - `input` elements inside mat-form-field\n * - `textarea` elements inside mat-form-field\n * - `mat-select` elements inside mat-form-field\n *\n * **Note:** Elements outside mat-form-field will NOT be skipped, even if\n * their tag matches the exclusion list.\n *\n * @param element - The focused element to check\n * @returns true if element should be skipped (no halo), false if halo should be shown\n */\n shouldSkipElementInMatFormField(element: HTMLElement): boolean {\n const tagName = element.tagName.toLowerCase();\n const isExcludedElement = haloExcludedElementsInMatFormField.includes(tagName);\n\n if (!isExcludedElement) {\n return false;\n }\n\n return this.isElementInsideMatFormField(element);\n }\n\n /**\n * Determines if an element should display the halo based on :focus-visible state.\n *\n * This method respects the CSS :focus-visible pseudo-class, which indicates that\n * focus was triggered by keyboard navigation (not mouse clicks). The halo only appears\n * when both conditions are met:\n * 1. Element matches :focus-visible (browser determines this)\n * 2. Last user interaction was via keyboard (tracked by service)\n *\n * **Fallback:** If browser doesn't support :focus-visible, falls back to checking\n * lastInteractionWasKeyboard only.\n *\n * @param element - The currently focused element\n * @param lastInteractionWasKeyboard - Whether the last interaction was a keyboard event\n * @returns true if halo should be visible, false otherwise\n */\n matchesFocusVisible(element: HTMLElement, lastInteractionWasKeyboard: boolean): boolean {\n try {\n const matches = element.matches(':focus-visible');\n if (!matches) return false;\n return matches && lastInteractionWasKeyboard;\n } catch {\n return lastInteractionWasKeyboard;\n }\n }\n\n /**\n * Checks if an element is actually visible on the page.\n *\n * Performs comprehensive visibility checks including:\n * - Element has non-zero dimensions (width and height > 0)\n * - Element is not hidden via CSS (display: none, visibility: hidden, opacity: 0)\n * - Element is not clipped by parent containers with overflow: hidden/clip\n * - All parent elements in the hierarchy are visible\n *\n * This prevents the halo from appearing around technically focused but visually\n * hidden elements (e.g., elements in collapsed sections, off-screen elements).\n *\n * @param element - Element to check for visibility\n * @returns true if element is visible to the user, false otherwise\n */\n isElementVisible(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect();\n\n // Check if element has dimensions\n if (rect.width === 0 || rect.height === 0) {\n return false;\n }\n\n // Check computed style on element itself\n const style = getComputedStyle(element);\n if (style.visibility === 'hidden' || style.display === 'none' || parseFloat(style.opacity) === 0) {\n return false;\n }\n\n // Check if element is clipped by overflow:hidden parent OR if any parent is hidden\n return this.isParentVisible(element, rect);\n }\n\n /**\n * Recursively checks if all parent elements are visible and not clipping the target element.\n *\n * Traverses up the DOM tree from the element to document.body, checking each parent for:\n * - Hidden state (display: none, visibility: hidden, opacity: 0)\n * - Overflow clipping (overflow: hidden/clip) that completely hides the element\n *\n * If any parent has overflow clipping, calculates the visible intersection area.\n * Returns false if element is completely clipped (no visible area).\n *\n * @param element - The element whose parents should be checked\n * @param rect - The bounding rectangle of the element\n * @returns true if element has visible area within all parent containers, false otherwise\n */\n isParentVisible(element: HTMLElement, rect: DOMRect): boolean {\n let parent = element.parentElement;\n while (parent && parent !== document.body) {\n const parentStyle = getComputedStyle(parent);\n\n // Check if parent is hidden\n if (parentStyle.display === 'none' || parentStyle.visibility === 'hidden' || parseFloat(parentStyle.opacity) === 0) {\n return false;\n }\n\n const parentOverflow = parentStyle.overflow + parentStyle.overflowX + parentStyle.overflowY;\n\n if (parentOverflow.includes('hidden') || parentOverflow.includes('clip')) {\n const parentRect = parent.getBoundingClientRect();\n // Calculate intersection - if there's no visible area, element is hidden\n const visibleRight = Math.min(rect.right, parentRect.right);\n const visibleLeft = Math.max(rect.left, parentRect.left);\n const visibleBottom = Math.min(rect.bottom, parentRect.bottom);\n const visibleTop = Math.max(rect.top, parentRect.top);\n\n const visibleWidth = visibleRight - visibleLeft;\n const visibleHeight = visibleBottom - visibleTop;\n\n // If no intersection or intersection is too small (less than 1px), element is not visible\n if (visibleWidth <= 0 || visibleHeight <= 0) {\n return false;\n }\n }\n parent = parent.parentElement;\n }\n return true;\n }\n\n //#endregion\n\n // #region Utilities\n\n /**\n * Generates the complete CSS styles for the halo element, merging defaults with custom config.\n *\n * Takes the base styles from `haloFocusStyles` constant and overrides specific properties\n * if custom configuration is provided. This allows global style customization while\n * maintaining all required base styles.\n *\n * **Configurable Properties:**\n * - Inner color (via config.innerColor)\n * - Outer color (via config.outerColor)\n *\n * **Non-configurable Properties:**\n * - position, pointerEvents, zIndex, opacity, transform (always use defaults)\n *\n * @param config - Optional global configuration object\n * @returns Complete CSS style declaration object for the halo element\n */\n getHaloFocusStyles(config?: HaloFocusConfig): Partial<CSSStyleDeclaration> {\n return {\n ...haloFocusStyles,\n border: config?.innerColor ? `2px solid ${config.innerColor}` : haloFocusStyles.border,\n boxShadow: config?.outerColor ? `0 0 0 4px ${config.outerColor}` : haloFocusStyles.boxShadow\n };\n }\n\n /**\n * Finds the nearest parent element marked as a halo container.\n *\n * Traverses up the DOM tree looking for an element with the `halo-container` attribute.\n * Container elements can define default halo styles (color, offset) that apply to all\n * their focused children, unless overridden by child-specific halo-* attributes.\n *\n * **Search Scope:**\n * - Starts from element's immediate parent\n * - Stops at document.body (does not check body itself)\n * - Returns first matching ancestor\n *\n * @param element - The focused element to search from\n * @returns The nearest halo container element, or null if none found\n */\n getHaloContainer(element: HTMLElement): HTMLElement | null {\n let parent = element.parentElement;\n while (parent && parent !== document.body) {\n if (parent.hasAttribute('halo-container')) {\n return parent;\n }\n parent = parent.parentElement;\n }\n return null;\n }\n\n /**\n * Converts any CSS color format to rgba with specified alpha transparency.\n *\n * Handles multiple color formats:\n * - **rgba**: Replaces existing alpha value\n * - **rgb**: Converts to rgba by appending alpha\n * - **Hex** (#RRGGBB): Converts to rgba format\n * - **Named colors**: Uses modern CSS `color-mix` function with alpha\n *\n * Used internally to create outer colors with reduced opacity from inner colors.\n *\n * @param color - CSS color in any valid format (rgba, rgb, hex, named)\n * @param alpha - Alpha transparency value (0-1)\n * @returns Color in rgba format with specified alpha\n */\n getColorWithAlpha(color: string, alpha: number): string {\n // If already rgba, try to replace alpha\n if (color.startsWith('rgba')) {\n return color.replace(/[\\d.]+\\)$/, `${alpha})`);\n }\n\n // If rgb, convert to rgba\n if (color.startsWith('rgb')) {\n return color.replace('rgb', 'rgba').replace(')', `, ${alpha})`);\n }\n\n // If hex color, convert to rgba\n if (color.startsWith('#')) {\n const hex = color.replace('#', '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n\n // For named colors or other formats, wrap in rgba with color-mix (modern approach)\n // Fallback to appending alpha as hex if browser doesn't support color-mix\n return `rgba(from ${color} r g b / ${alpha})`;\n }\n\n /**\n * Calculates the offset (distance) between the halo border and element edges.\n *\n * Determines offset using the following priority order:\n * 1. **Element's own attributes** (highest priority):\n * - `halo-offset=\"value\"` - Custom numeric value\n * 2. **Parent container attributes** (if element has no offset):\n * - `halo-container-offset=\"value\"`\n * 3. **Default offset** (if nothing specified): 3px\n *\n * @param element - The focused element to calculate offset for\n * @returns Offset value in pixels\n */\n getTargetOffset(element: HTMLElement | null): number {\n if (!element) return defaultHaloFocusOffset;\n\n // 1. Check element's own attributes first\n const customOffset = element.getAttribute('halo-offset');\n if (customOffset !== null) {\n const parsed = parseFloat(customOffset);\n if (!isNaN(parsed)) return parsed;\n }\n\n // 2. Check container attributes as fallback\n const container = this.getHaloContainer(element);\n if (container) {\n const containerOffset = container.getAttribute('halo-container-offset');\n if (containerOffset !== null) {\n const parsed = parseFloat(containerOffset);\n if (!isNaN(parsed)) return parsed;\n }\n }\n\n // 3. Default: positive offset (halo outside element)\n return defaultHaloFocusOffset;\n }\n\n /**\n * Applies custom color styling to the halo element based on element or container attributes.\n *\n * Color resolution priority:\n * 1. **Element's own color**: `halo-color=\"color\"` attribute (highest priority)\n * 2. **Container's color**: `halo-container-color=\"color\"` from nearest halo container\n * 3. **Default color**: From global config or base styles (if no custom color found)\n *\n * When custom color is found:\n * - Sets inner color (2px solid border) with the custom color\n * - Sets outer color (shadow) to match, with 20% alpha transparency for subtle depth\n *\n * When no custom color:\n * - Resets to default styles from config or haloFocusStyles constant\n *\n * @param element - The focused element (checked for halo-color attribute)\n * @param haloElement - The halo DIV element to apply styles to\n * @param config - Optional global config for default colors\n */\n setCustomColor(element: HTMLElement, haloElement: HTMLDivElement, config?: HaloFocusConfig): void {\n // 1. Check element's own color first\n let customColor = element.getAttribute('halo-color');\n\n // 2. If not found, check container color\n if (!customColor) {\n const container = this.getHaloContainer(element);\n if (container) {\n customColor = container.getAttribute('halo-container-color');\n }\n }\n // Apply color or reset to default\n if (customColor) {\n haloElement.style.border = `2px solid ${customColor}`;\n haloElement.style.boxShadow = `0 0 0 4px ${this.getColorWithAlpha(customColor, 0.2)}`;\n } else {\n // Reset to default styles\n haloElement.style.border = this.getHaloFocusStyles(config).border!;\n haloElement.style.boxShadow = this.getHaloFocusStyles(config).boxShadow!;\n }\n }\n\n /**\n * Positions and sizes the halo element to surround the focused element.\n *\n * Calculates halo dimensions and position based on:\n * - Element's bounding rectangle (viewport-relative position)\n * - Calculated offset from getTargetOffset() (custom or default)\n * - Element's border-radius for matching rounded corners\n *\n * **Calculation Details:**\n * - Position: Element's top/left minus offset (for outside positioning)\n * - Size: Element's width/height plus 2× offset (to extend on all sides)\n * - Border radius: Inherited from focused element's computed style\n *\n * Uses Math.floor for position and Math.ceil for dimensions to prevent\n * subpixel rendering issues and ensure full element coverage.\n *\n * @param currentElement - The currently focused element to surround\n * @param haloElement - The halo DIV element to position and size\n */\n setHaloElementSize(currentElement: HTMLElement, haloElement: HTMLDivElement): void {\n const rect = currentElement.getBoundingClientRect();\n const offset = this.getTargetOffset(currentElement);\n\n const left = Math.floor(rect.left - offset);\n const top = Math.floor(rect.top - offset);\n const width = Math.ceil(rect.width + offset * 2);\n const height = Math.ceil(rect.height + offset * 2);\n\n const cs = getComputedStyle(currentElement);\n haloElement.style.left = `${left}px`;\n haloElement.style.top = `${top}px`;\n haloElement.style.width = `${width}px`;\n haloElement.style.height = `${height}px`;\n haloElement.style.borderRadius = cs.borderRadius || '8px';\n }\n\n //#endregion\n}\n","import { Component, inject, Injectable, signal } from '@angular/core';\nimport { MAT_SNACK_BAR_DATA, MatSnackBar, MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel, MatSnackBarRef } from '@angular/material/snack-bar';\nimport { SnackBarData, SnackBarLevel, SnackBarOptions } from '../../models'\nimport { MatButtonModule } from '@angular/material/button';\n@Injectable({\n providedIn: 'root'\n})\nexport class SnackBarService {\n #snackBar = inject(MatSnackBar);\n\n info(message: string, action?: string, duration?: number): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'info', ...(duration ? { duration } : {}) });\n }\n\n success(message: string, action?: string, duration?: number): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'success', ...(duration ? { duration } : {}) });\n }\n\n warning(message: string, action?: string, duration?: number): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'warning', ...(duration ? { duration } : {}) });\n }\n\n danger(message: string, action?: string, duration?: number): MatSnackBarRef<SnackBarComponent> {\n return this.snack(message, { action, level: 'danger', ...(duration ? { duration } : {}) });\n }\n\n snack(message: string, options: SnackBarOptions): MatSnackBarRef<SnackBarComponent> {\n return this.#snackBar.openFromComponent(SnackBarComponent, {\n data: {\n level: options.level,\n message,\n action: options.action\n },\n ...(options.duration ? { duration: options.duration } : {}),\n horizontalPosition: options.horizontalPosition,\n verticalPosition: options.verticalPosition,\n panelClass: ['yuv-snack-bar', 'level-' + options.level]\n });\n }\n}\n\n@Component({\n selector: 'yuv-snack-bar-component',\n template: `\n <span matSnackBarLabel (click)=\"dismiss()\"> {{ message() }} </span>\n @let a = action();\n @if (a) {\n <span matSnackBarActions>\n <button mat-button matSnackBarAction (click)=\"dismiss(!!a)\">{{ action() }}</button>\n </span>\n }\n `,\n styles: `\n :host {\n display: flex;\n }\n `,\n imports: [MatButtonModule, MatSnackBarLabel, MatSnackBarActions, MatSnackBarAction],\n host: {\n '[class.info]': \"level() === 'info'\",\n '[class.success]': \"level() === 'success'\",\n '[class.warning]': \"level() === 'warning'\",\n '[class.danger]': \"level() === 'danger'\"\n }\n})\nexport class SnackBarComponent {\n #snackBarRef = inject(MatSnackBarRef);\n #snackData = inject<SnackBarData>(MAT_SNACK_BAR_DATA);\n level = signal<SnackBarLevel>(this.#snackData.level);\n message = signal<string>(this.#snackData.message);\n action = signal<string | undefined>(this.#snackData.action);\n\n dismiss(withAction = false) {\n if (withAction) {\n this.#snackBarRef.dismissWithAction();\n } else if (!this.action()) {\n this.#snackBarRef.dismiss();\n }\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { Subscription, debounceTime, timer, Observable, switchMap, map, finalize } from 'rxjs';\nimport { TranslateService } from '@ngx-translate/core';\nimport { ChannelMessage } from '../../enums';\nimport { SnackBarComponent, SnackBarService } from '../snack-bar/snack-bar.service';\nimport { AppCacheService, BackendService, UserService } from '@yuuvis/client-core';\nimport { MatSnackBarRef } from '@angular/material/snack-bar';\nimport { ChannelPayload } from '../../models';\nimport { sessionActivityWindowBeforeEnd, sessionDefaultDuration, sessionPopupBeforeEnd } from '../../config';\n\n/**\n * Manages client-side session expiry: persists expiration, tracks user and HTTP activity,\n * shows a pre-expiry popup with an extend CTA, and syncs state across tabs via BroadcastChannel.\n *\n * Key behaviors\n * - Persists `expiresAt` in AppCacheService so multiple tabs share the same deadline\n * - Automatically listens to BackendService HTTP activity (debounced) to extend sessions\n * - Extends backend session by calling `/api-web/api/idm/whoami` endpoint (skipped when triggered by HTTP activity)\n * - User activity (mouse, keyboard, scroll) inside a defined window will auto-extend the session\n * - Displays a snack popup shortly before expiry; user can extend from the popup\n * - Broadcasts `SessionExtended` / `SessionLogout` to keep tabs in sync\n *\n * Setup options\n *\n * Option 1: Known session duration at startup\n * Use when the session duration is known in advance and remains constant.\n * ```ts\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideSession(30 * 60 * 1000), // 30 minutes - set at app startup\n * ]\n * };\n * ```\n *\n * Option 2: Dynamic session duration from backend\n * Use when the session duration is determined after login (e.g., from backend response).\n * ```ts\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideSession(), // Initialize without duration (defaults to 30 minutes)\n * ]\n * };\n *\n * // After login in AuthService:\n * login().subscribe(response => {\n * const sessionDuration = response.sessionExpiresIn; // from backend\n * sessionService.startSession(sessionDuration);\n * });\n * ```\n * Note: If no duration is provided to provideSession(), a default of 30 minutes is used until startSession() is called.\n *\n * Lifecycle notes\n * - HTTP activity is automatically tracked via BackendService.httpCommunicationOccurred$\n * - Backend session is extended via whoami endpoint, except when triggered by HTTP activity or cross-tab sync\n * - Internal flag prevents recursive whoami calls when the endpoint itself triggers httpCommunicationOccurred$\n * - Warning and logout timers are reset on every extend\n * - All timers and the popup are cleared on logout\n * - Broadcast messages are listened for and mirrored across tabs\n * - For UI activity, this service listens to DOM events (mousemove, keydown, click, scroll)\n */\n@Injectable({ providedIn: 'root' })\nexport class SessionService {\n //#region Dependencies\n\n /**\n * AppCacheService is used to persist the session expiry timestamp in localStorage.\n *\n * Why persist to storage instead of runtime memory:\n * - Single source of truth: All browser tabs can read the same expiry value directly\n * from storage without inter-tab communication\n * - Avoids master/slave tab pattern: No need to elect one \"master\" tab to hold the\n * authoritative expiry time and sync it to others\n * - Survives page refresh: Users don't lose their session when refreshing the page\n * - Simplifies synchronization: BroadcastChannel is only used for notifications about\n * changes (extend/logout), not for maintaining shared state\n * - Consistent behavior: Every tab independently calculates timers based on the same\n * persisted expiry value, ensuring uniform warning/logout timing\n */\n #appCacheService = inject(AppCacheService);\n #snackBarService = inject(SnackBarService);\n #backendService = inject(BackendService);\n #userService = inject(UserService);\n private translate = inject(TranslateService);\n\n //#endregion\n\n //#region Properties\n\n #sessionDuration = sessionDefaultDuration;\n\n readonly #sessionStorageKey = 'session-expires-at';\n readonly #activityWindowBeforeEnd = sessionActivityWindowBeforeEnd;\n readonly #popupBeforeEnd = sessionPopupBeforeEnd;\n\n #activityWindowStart$?: Subscription;\n #activityWindowEnd$?: Subscription;\n #logoutTimer$?: Subscription;\n #httpActivitySubscription$?: Subscription;\n\n #activityDetectedInWindow = false;\n #trackingWindowActivity = false;\n #userActivityTrackingInitialized = false;\n #extendingSessionViaBackend = false;\n\n #sessionChannel = new BroadcastChannel('session_channel');\n #snackReference?: MatSnackBarRef<SnackBarComponent>;\n\n //#endregion\n\n //#region Public methods\n\n /**\n * Initializes cross-tab listeners and activity hooks.\n *\n * IMPORTANT: This is automatically called by `provideSession()` via APP_INITIALIZER.\n * You should NOT call this manually - just add `provideSession()` to your app providers.\n *\n * What it does:\n * - Initializes session with provided or default duration value\n * - Wires BroadcastChannel subscriptions for cross-tab sync\n * - Subscribes to BackendService.httpCommunicationOccurred$ for automatic HTTP activity tracking\n * - Attaches DOM event listeners (mousemove, keydown, click, scroll) for the activity window\n */\n init(sessionDuration?: number): void {\n this.startSession(sessionDuration ?? sessionDefaultDuration);\n this.listenToChannel();\n this.setupHttpDebounce();\n this.setupUserActivityTracking();\n }\n\n /**\n * Sets the session duration and starts the session lifecycle.\n *\n * Use this method when you need to set or update the session duration dynamically,\n * typically after receiving authentication/session information from the backend.\n *\n * This is the correct approach when:\n * - Session duration is not known at application startup\n * - Duration comes from a backend API response after login\n * - You need to manually override the duration set via provideSession()\n *\n * Example:\n * ```ts\n * // After login in AuthService\n * login().subscribe(response => {\n * this.sessionService.startSession(response.sessionExpiresIn);\n * });\n * ```\n *\n * @param duration Total session length in milliseconds\n */\n startSession(duration: number): void {\n this.#sessionDuration = duration;\n this.extendSession(false, undefined, true);\n }\n\n /**\n * Extends the session expiry, resets all timers, and optionally broadcasts to other tabs.\n *\n * @param broadcast When true (default), posts a BroadcastChannel message so other tabs sync\n * @param expiresAt Optional custom expiry timestamp; if not provided, calculates based on session duration\n * @param skipBackendCall When true, skips the whoami backend call (used when already triggered by HTTP activity)\n */\n extendSession(broadcast = true, expiresAt?: number, skipBackendCall = false): void {\n const newExpiresAt = expiresAt ?? Date.now() + this.#sessionDuration;\n\n this.setExpiresAt(newExpiresAt);\n this.resetAllTimers();\n\n if (!skipBackendCall && !this.#extendingSessionViaBackend) {\n this.#extendingSessionViaBackend = true;\n this.#backendService\n .get<unknown>('/idm/whoami')\n .pipe(\n finalize(() => {\n // Keep flag true longer than debounce time to prevent duplicate broadcasts\n // from httpCommunicationOccurred$ triggered by this whoami call\n setTimeout(() => {\n this.#extendingSessionViaBackend = false;\n }, 600);\n if (broadcast) {\n this.#sessionChannel.postMessage({\n type: ChannelMessage.SessionExtended,\n expiresAt: newExpiresAt\n });\n }\n })\n )\n .subscribe();\n } else if (broadcast) {\n this.#sessionChannel.postMessage({\n type: ChannelMessage.SessionExtended,\n expiresAt: newExpiresAt\n });\n }\n }\n\n //#endregion\n\n //#region Core Logic\n\n private listenToChannel(): void {\n this.#sessionChannel.onmessage = (event: MessageEvent<ChannelPayload>) => {\n const message = event.data;\n\n switch (message.type) {\n case ChannelMessage.SessionExtended:\n this.extendSession(false, message.expiresAt, true);\n break;\n\n case ChannelMessage.SessionLogout:\n this.performLogout(false);\n break;\n }\n };\n }\n\n private scheduleActivityWindow(): void {\n this.getExpiresAt().subscribe((expiresAt) => {\n const activityWindowStartIn = expiresAt - Date.now() - this.#activityWindowBeforeEnd;\n const activityWindowEndIn = expiresAt - Date.now() - this.#popupBeforeEnd;\n\n this.#activityWindowStart$ = timer(Math.max(activityWindowStartIn, 0)).subscribe(() => {\n this.#trackingWindowActivity = true;\n this.#activityDetectedInWindow = false;\n });\n\n this.#activityWindowEnd$ = timer(Math.max(activityWindowEndIn, 0)).subscribe(() => {\n this.#trackingWindowActivity = false;\n\n if (this.#activityDetectedInWindow) {\n this.extendSession();\n } else {\n this.showPopup();\n }\n });\n });\n }\n\n private showPopup(): void {\n if (this.#snackReference) return;\n\n const message = this.translate.instant('yuv.session.expires.message');\n const action = this.translate.instant('yuv.session.extend.action');\n this.#snackReference = this.#snackBarService.warning(message, action);\n\n this.scheduleLogout();\n\n this.#snackReference.onAction().subscribe(() => {\n this.#snackReference?.dismiss();\n this.extendSession();\n });\n\n this.#snackReference.afterDismissed().subscribe(() => {\n this.#snackReference = undefined;\n });\n }\n\n private scheduleLogout(): void {\n this.#logoutTimer$?.unsubscribe();\n\n this.#logoutTimer$ = this.getExpiresAt()\n .pipe(switchMap((expiresAt) => timer(Math.max(expiresAt - Date.now(), 0)).pipe(map(() => expiresAt))))\n .subscribe((expiresAt) => {\n if (Date.now() >= expiresAt) {\n this.performLogout(true);\n }\n });\n }\n\n private performLogout(broadcast = true): void {\n this.clearTimers();\n this.#snackReference?.dismiss();\n\n if (broadcast) {\n this.#sessionChannel.postMessage({\n type: ChannelMessage.SessionLogout\n });\n }\n\n this.#userService.logout();\n }\n\n //#endregion\n\n //#region Activity + HTTP\n\n private setupHttpDebounce(): void {\n this.#httpActivitySubscription$?.unsubscribe();\n this.#httpActivitySubscription$ = this.#backendService.httpCommunicationOccurred$.pipe(debounceTime(500)).subscribe(() => {\n if (!this.#extendingSessionViaBackend) {\n this.extendSession(true, undefined, true);\n }\n });\n }\n\n private setupUserActivityTracking(): void {\n if (this.#userActivityTrackingInitialized) return;\n\n ['mousemove', 'keydown', 'click', 'scroll'].forEach((event) =>\n window.addEventListener(event, () => {\n if (this.#trackingWindowActivity) {\n this.#activityDetectedInWindow = true;\n }\n })\n );\n this.#userActivityTrackingInitialized = true;\n }\n\n //#endregion\n\n //#region Utilities\n\n private resetAllTimers(): void {\n this.clearTimers();\n this.scheduleActivityWindow();\n this.#snackReference?.dismiss();\n }\n\n private getExpiresAt(): Observable<number> {\n return this.#appCacheService.getItem(this.#sessionStorageKey);\n }\n\n private setExpiresAt(expiresAt: number): void {\n this.#appCacheService.setItem(this.#sessionStorageKey, expiresAt).subscribe();\n }\n\n private clearTimers(): void {\n this.#activityWindowStart$?.unsubscribe();\n this.#activityWindowEnd$?.unsubscribe();\n this.#logoutTimer$?.unsubscribe();\n this.#httpActivitySubscription$?.unsubscribe();\n }\n\n //#endregion\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders, NgZone, provideAppInitializer } from '@angular/core';\nimport { HaloFocusService, HaloUtilityService } from '../../services';\nimport { HaloFocusConfig } from '../../models';\n\n/**\n * Provides and initializes the Halo Focus feature for the Angular application.\n *\n * This function sets up a global visual accessibility enhancement that creates a\n * \"halo\" border around keyboard-focused elements, making navigation more visible\n * and improving the user experience for keyboard users.\n *\n * **What It Does:**\n * - Registers HaloFocusService and HaloUtility as application-wide providers\n * - Automatically initializes the service during app startup via APP_INITIALIZER\n * - Runs initialization outside Angular zone for optimal performance\n * - Creates a persistent halo element that tracks focus throughout the application\n *\n * **Key Features:**\n * - Keyboard-only focus indication (respects CSS :focus-visible)\n * - Performance-optimized with requestAnimationFrame and zone-free execution\n * - Fully customizable via global config or per-element attributes\n * - Supports container-level defaults for consistent styling\n * - Automatic visibility detection and responsive to DOM changes\n *\n * **Configuration Options:**\n * @param config - Optional global configuration object\n * @param config.innerColor - Inner color of the halo (default: 'var(--ymt-primary)')\n * @param config.outerColor - Outer color around the halo (default: 'rgba(from var(--ymt-primary) r g b / 0.2)')\n *\n * @returns EnvironmentProviders for the Halo Focus feature\n *\n * @example\n * // Basic usage in app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHaloFocus()\n * ]\n * };\n *\n * @example\n * // With custom configuration\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHaloFocus({\n * innerColor: '#007bff',\n * outerColor: 'rgba(0, 123, 255, 0.25)'\n * })\n * ]\n * };\n *\n * @example\n * // Using element-level attributes in your template\n * <button halo-color=\"red\">Custom Red Halo</button>\n * <input halo-offset=\"5\" />\n * <select halo-skip>No Halo</select>\n *\n * @example\n * // Using container-level attributes\n * <form halo-container halo-container-color=\"blue\" halo-container-offset=\"5\">\n * <input /> <!-- Will use blue color and 5px offset -->\n * <button halo-color=\"red\">Submit</button> <!-- Overrides with red -->\n * </form>\n */\nexport function provideHaloFocus(config?: HaloFocusConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n HaloUtilityService,\n provideAppInitializer(() => {\n const zone = inject(NgZone);\n const haloFocus = inject(HaloFocusService);\n zone.runOutsideAngular(() => haloFocus.init(config));\n })\n ]);\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';\nimport { SessionService } from '../../services/session/session.service';\n\n\n/**\n * Provides and initializes the SessionService at application startup.\n *\n * What it does\n * - Registers SessionService as a singleton to manage session expiry across the app\n * - Runs `session.init()` via APP_INITIALIZER when the app boots\n * - Initializes cross-tab BroadcastChannel sync, HTTP debounce hooks, and user-activity listeners\n * - Automatically tracks HTTP activity and user interactions to extend sessions\n *\n * Usage scenarios\n *\n * Scenario 1: Known session duration at startup\n * When the session duration is predetermined and constant, provide it here.\n * ```ts\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideSession(30 * 60 * 1000), // 30 minutes\n * ]\n * };\n * ```\n *\n * Scenario 2: Dynamic session duration from backend\n * When the session duration is determined after login (e.g., from backend response),\n * omit the parameter here and call `startSession()` after receiving the duration.\n * ```ts\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideSession(), // Defaults to 30 minutes until startSession() is called\n * ]\n * };\n *\n * // After login in AuthService:\n * login().subscribe(response => {\n * sessionService.startSession(response.sessionExpiresIn); // Set actual duration\n * });\n * ```\n *\n * @param sessionDuration Optional session duration in milliseconds. If omitted, defaults to 30 minutes (1800000ms).\n */\nexport function provideSession(sessionDuration?: number): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideAppInitializer(() => {\n const session = inject(SessionService);\n session.init(sessionDuration);\n })\n ]);\n}\n","import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@NgModule({\n imports: [CommonModule],\n})\nexport class YuuvisClientFrameworkModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -28,7 +28,6 @@ export declare class DatetimeComponent extends AbstractMatFormField<Date | null>
|
|
|
28
28
|
* Enables setting time as well (default: false)
|
|
29
29
|
*/
|
|
30
30
|
withTime: import("@angular/core").InputSignal<boolean>;
|
|
31
|
-
constructor();
|
|
32
31
|
openCalendar(): void;
|
|
33
32
|
propagateChange: (_: any) => void;
|
|
34
33
|
writeValue(value: any): void;
|
|
@@ -22,7 +22,7 @@ export declare class OrganizationComponent extends AbstractMatFormField<FormCont
|
|
|
22
22
|
readonly translate: TranslateService;
|
|
23
23
|
minLength: number;
|
|
24
24
|
busy: import("@angular/core").WritableSignal<boolean>;
|
|
25
|
-
acFormControl: FormControl<AutocompleteItem | AutocompleteItem[] | null | undefined>;
|
|
25
|
+
acFormControl: FormControl<AutocompleteItem<unknown> | AutocompleteItem<unknown>[] | null | undefined>;
|
|
26
26
|
ngControl: import("@angular/forms").NgControl | null;
|
|
27
27
|
_innerValue: OrganizationNode[];
|
|
28
28
|
set innerValue(iv: OrganizationNode[]);
|
|
@@ -43,7 +43,7 @@ export declare class OrganizationSetComponent extends AbstractMatFormField<strin
|
|
|
43
43
|
classifications: import("@angular/core").InputSignal<string[] | undefined>;
|
|
44
44
|
types: import("@angular/core").InputSignal<string[] | undefined>;
|
|
45
45
|
busy: import("@angular/core").WritableSignal<boolean>;
|
|
46
|
-
acFormControl: FormControl<AutocompleteItem | AutocompleteItem[] | null | undefined>;
|
|
46
|
+
acFormControl: FormControl<AutocompleteItem<unknown> | AutocompleteItem<unknown>[] | null | undefined>;
|
|
47
47
|
ngControl: import("@angular/forms").NgControl | null;
|
|
48
48
|
innerValue: OrganizationSetEntry[];
|
|
49
49
|
autocompleteRes: AutocompleteItem[];
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export * from './lib/
|
|
2
|
-
export * from './lib/
|
|
1
|
+
export * from './lib/config';
|
|
2
|
+
export * from './lib/enums';
|
|
3
|
+
export * from './lib/models';
|
|
3
4
|
export * from './lib/providers';
|
|
5
|
+
export * from './lib/services';
|
|
6
|
+
export * from './lib/yuuvis-client-framework.module';
|
package/lib/config/index.d.ts
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time window (in milliseconds) before session expiry when user activity triggers auto-extension.
|
|
3
|
+
*
|
|
4
|
+
* During this window, the service tracks user interactions (mouse, keyboard, scroll) and HTTP activity.
|
|
5
|
+
* If any activity is detected, the session is automatically extended, preventing the expiry warning popup.
|
|
6
|
+
*
|
|
7
|
+
* **Session Timeline:**
|
|
8
|
+
* ```
|
|
9
|
+
* [Session Start] ──────────────────────────────► [Session Expires]
|
|
10
|
+
* ▲ ▲
|
|
11
|
+
* │ └─ sessionPopupBeforeEnd (1 min)
|
|
12
|
+
* └─ Activity window starts (2 min)
|
|
13
|
+
*
|
|
14
|
+
* ┌─────────────────────────────────────────────┐
|
|
15
|
+
* │ Activity Window (2 minutes) │
|
|
16
|
+
* │ • Tracks: mousemove, keydown, click, scroll │
|
|
17
|
+
* │ • Tracks: HTTP requests (debounced) │
|
|
18
|
+
* │ • If activity detected → auto-extend │
|
|
19
|
+
* │ • If no activity → show warning popup │
|
|
20
|
+
* └─────────────────────────────────────────────┘
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* **Why This Matters:**
|
|
24
|
+
* - Prevents unnecessary interruptions for active users
|
|
25
|
+
* - Reduces server-side session renewal traffic by batching extensions
|
|
26
|
+
* - Provides a grace period before showing the expiry warning
|
|
27
|
+
*
|
|
28
|
+
* **Tracked Activities:**
|
|
29
|
+
* - **User interactions**: `mousemove`, `keydown`, `click`, `scroll` on `window`
|
|
30
|
+
* - **HTTP traffic**: Any request via `BackendService.httpCommunicationOccurred$` (debounced 500ms)
|
|
31
|
+
*
|
|
32
|
+
* **Interaction with Other Constants:**
|
|
33
|
+
* - Must be **greater than** {@link sessionPopupBeforeEnd} to allow the activity window before the popup
|
|
34
|
+
* - Typical relationship: `activityWindow > popupWarning` (e.g., 2 min > 1 min)
|
|
35
|
+
*
|
|
36
|
+
* **Performance Note:**
|
|
37
|
+
* Activity tracking is only active during this window, not throughout the entire session,
|
|
38
|
+
* minimizing performance impact of event listeners.
|
|
39
|
+
*
|
|
40
|
+
* @default 2 minutes (120000 milliseconds)
|
|
41
|
+
* @see {@link SessionService.setupUserActivityTracking} for implementation details
|
|
42
|
+
*/
|
|
43
|
+
export declare const sessionActivityWindowBeforeEnd: number;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default session duration (in milliseconds) when no explicit duration is provided.
|
|
3
|
+
*
|
|
4
|
+
* This value is used as the fallback session lifetime when:
|
|
5
|
+
* - `provideSession()` is called without a duration parameter
|
|
6
|
+
* - Before `startSession(duration)` is called with a backend-provided value
|
|
7
|
+
*
|
|
8
|
+
* **Timeline:**
|
|
9
|
+
* ```
|
|
10
|
+
* [Session Start] ──────────────────────────────► [Session Expires]
|
|
11
|
+
* ← sessionDefaultDuration →
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* **Override Methods:**
|
|
15
|
+
* - **At startup (known duration)**: `provideSession(customDuration)`
|
|
16
|
+
* - **After login (dynamic duration)**: `sessionService.startSession(backendDuration)`
|
|
17
|
+
*
|
|
18
|
+
* **Usage Scenarios:**
|
|
19
|
+
*
|
|
20
|
+
* Scenario 1 - Fixed duration:
|
|
21
|
+
* ```ts
|
|
22
|
+
* // app.config.ts
|
|
23
|
+
* providers: [
|
|
24
|
+
* provideSession(45 * 60 * 1000) // Override to 45 minutes
|
|
25
|
+
* ]
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Scenario 2 - Backend-provided duration:
|
|
29
|
+
* ```ts
|
|
30
|
+
* // app.config.ts
|
|
31
|
+
* providers: [
|
|
32
|
+
* provideSession() // Uses default 30 minutes initially
|
|
33
|
+
* ]
|
|
34
|
+
*
|
|
35
|
+
* // auth.service.ts (after login)
|
|
36
|
+
* login().subscribe(response => {
|
|
37
|
+
* sessionService.startSession(response.sessionExpiresIn); // Update to backend value
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* **Related Constants:**
|
|
42
|
+
* - {@link sessionActivityWindowBeforeEnd} - When to start tracking user activity
|
|
43
|
+
* - {@link sessionPopupBeforeEnd} - When to show the expiry warning popup
|
|
44
|
+
*
|
|
45
|
+
* @default 30 minutes (1800000 milliseconds)
|
|
46
|
+
*/
|
|
47
|
+
export declare const sessionDefaultDuration: number;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time (in milliseconds) before session expiry when the warning popup is displayed.
|
|
3
|
+
*
|
|
4
|
+
* When this threshold is reached without detected user activity during the activity window,
|
|
5
|
+
* a snackbar notification appears, warning the user that their session will expire soon
|
|
6
|
+
* and providing an "Extend session" action button.
|
|
7
|
+
*
|
|
8
|
+
* **Session Timeline:**
|
|
9
|
+
* ```
|
|
10
|
+
* [Session Start] ──────────────────────────────► [Session Expires]
|
|
11
|
+
* ▲ ▲
|
|
12
|
+
* │ └─ Popup appears (1 min before expiry)
|
|
13
|
+
* └─ Activity window starts (2 min before expiry)
|
|
14
|
+
*
|
|
15
|
+
* User has no activity during the 2-minute window:
|
|
16
|
+
* ├─ 2 min before: Activity tracking starts
|
|
17
|
+
* ├─ 1 min before: ⚠️ Popup shows "Session expires in one minute"
|
|
18
|
+
* └─ 0 min: Auto-logout if no action taken
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* **Popup Behavior:**
|
|
22
|
+
* - Displays a warning message: "Session expires in one minute"
|
|
23
|
+
* - Provides an action button: "Extend session"
|
|
24
|
+
* - Clicking "Extend session" → extends session by the full session duration
|
|
25
|
+
* - Ignoring the popup → automatic logout when timer reaches zero
|
|
26
|
+
* - Only one popup is shown at a time (subsequent extensions dismiss the existing popup)
|
|
27
|
+
*
|
|
28
|
+
* **Cross-Tab Synchronization:**
|
|
29
|
+
* If the user extends the session in one browser tab, all other tabs:
|
|
30
|
+
* - Automatically dismiss their popups (if shown)
|
|
31
|
+
* - Reset their timers to the new expiry time
|
|
32
|
+
* - Avoid showing redundant warnings
|
|
33
|
+
*
|
|
34
|
+
* **Interaction with Other Constants:**
|
|
35
|
+
* - Must be **less than** {@link sessionActivityWindowBeforeEnd}
|
|
36
|
+
* - Typical relationship: `activityWindow > popupWarning` (e.g., 2 min > 1 min)
|
|
37
|
+
* - The gap between them determines how long the activity window runs before the popup
|
|
38
|
+
*
|
|
39
|
+
* **UX Considerations:**
|
|
40
|
+
* - **1 minute** gives users enough time to react without being too intrusive
|
|
41
|
+
* - Too short (e.g., 10 seconds): Users may not have time to respond
|
|
42
|
+
* - Too long (e.g., 5 minutes): Users may be unnecessarily interrupted if they return
|
|
43
|
+
*
|
|
44
|
+
* @default 1 minute (60000 milliseconds)
|
|
45
|
+
* @see {@link SessionService.showPopup} for popup implementation
|
|
46
|
+
*/
|
|
47
|
+
export declare const sessionPopupBeforeEnd: number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './channel-message.enum';
|
package/lib/models/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './channel-payload.model';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type SnackBarLevel = 'info' | 'success' | 'warning' | 'danger';
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { MatSnackBarHorizontalPosition, MatSnackBarVerticalPosition } from "@angular/material/snack-bar";
|
|
2
|
-
|
|
3
|
-
export interface SnackBarData {
|
|
4
|
-
level: SnackBarLevel;
|
|
5
|
-
message: string;
|
|
6
|
-
action?: string;
|
|
7
|
-
}
|
|
2
|
+
import { SnackBarLevel } from "./snack-bar-level.model";
|
|
8
3
|
export interface SnackBarOptions {
|
|
9
4
|
duration?: number;
|
|
10
5
|
horizontalPosition?: MatSnackBarHorizontalPosition;
|
package/lib/providers/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './provide-session.provider';
|