UNPKG

@sixbell-telco/sdk

Version:

A collection of reusable components designed for use in Sixbell Telco Angular projects

1 lines 11 kB
{"version":3,"file":"sixbell-telco-sdk-directives-auto-focus.mjs","sources":["../../../projects/sdk/directives/auto-focus/src/autofocus.directive.ts","../../../projects/sdk/directives/auto-focus/sixbell-telco-sdk-directives-auto-focus.ts"],"sourcesContent":["import { AfterViewInit, Directive, ElementRef, OnDestroy, effect, input, signal } from '@angular/core';\n\n/**\n * Directive that automatically focuses an element when it becomes visible.\n *\n * This directive uses multiple strategies to detect when an element becomes visible:\n * - IntersectionObserver to detect when the element enters the viewport\n * - MutationObserver to detect DOM changes that might affect visibility\n * - Polling at regular intervals as a fallback mechanism\n *\n * The directive works reliably with modals, dialogs, accordions, and other UI components\n * that dynamically show and hide content.\n *\n * @example\n * ```html\n * <!-- Basic usage -->\n * <input [stAutofocus]=\"true\" />\n *\n * <!-- Conditional autofocus -->\n * <input [stAutofocus]=\"shouldFocus\" />\n *\n * <!-- In a form inside a modal -->\n * <st-modal>\n * <input [stAutofocus]=\"true\" />\n * </st-modal>\n * ```\n *\n * @usageNotes\n * The directive takes a boolean input that determines whether autofocus should be applied.\n * When this value is true, the directive will attempt to focus the element once it becomes visible,\n * and will continue to monitor the element's visibility state in case it changes.\n *\n * This is particularly useful for:\n * - Elements in modals that need focus when opened\n * - Elements in accordions/collapsible sections that should receive focus when expanded\n * - Elements that are conditionally rendered and need focus when they appear\n *\n * @publicApi\n */\n@Directive({\n\tselector: '[stAutofocus]',\n})\nexport class AutofocusDirective implements AfterViewInit, OnDestroy {\n\t/**\n\t * Controls whether autofocus should be applied to the element.\n\t * Set to true to enable autofocus.\n\t */\n\tfocus = input<boolean>(false);\n\n\t/** Tracks the current visibility state of the element */\n\tprivate isVisible = signal(false);\n\n\t/** Forces effect to run when visibility changes, even to the same value */\n\tprivate changeTimestamp = signal(0);\n\n\t/** Collection of observers for cleanup */\n\tprivate observers: (IntersectionObserver | MutationObserver)[] = [];\n\n\t/** Interval used for polling visibility */\n\tprivate pollingInterval: number | null = null;\n\n\t/** Flag to prevent concurrent visibility checks */\n\tprivate checkingVisibility = false;\n\n\t/** Timeout reference for focus operation */\n\tprivate focusTimeout: number | null = null;\n\n\tconstructor(private elementRef: ElementRef) {\n\t\t// Effect that triggers focus when element becomes visible\n\t\teffect(() => {\n\t\t\tconst visible = this.isVisible();\n\t\t\tconst timestamp = this.changeTimestamp();\n\n\t\t\tif (this.focus() && visible) {\n\t\t\t\t// Clear any existing timeout first\n\t\t\t\tif (this.focusTimeout !== null) {\n\t\t\t\t\tclearTimeout(this.focusTimeout);\n\t\t\t\t}\n\n\t\t\t\t// Store the timeout reference\n\t\t\t\tthis.focusTimeout = window.setTimeout(() => {\n\t\t\t\t\tthis.elementRef.nativeElement.focus();\n\t\t\t\t}, 50);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Sets up visibility observers after the view is initialized\n\t */\n\tngAfterViewInit() {\n\t\tif (this.focus()) {\n\t\t\t// Set up both observer types\n\t\t\tthis.setupIntersectionObserver();\n\t\t\tthis.setupMutationObserver();\n\n\t\t\t// Lightweight polling (every 500ms) as fallback for edge cases\n\t\t\tthis.pollingInterval = window.setInterval(() => {\n\t\t\t\tthis.checkVisibility(true);\n\t\t\t}, 500);\n\n\t\t\t// Initial visibility check\n\t\t\tthis.checkVisibility(true);\n\t\t}\n\t}\n\n\t/**\n\t * Cleans up all observers and timers to prevent memory leaks\n\t */\n\tngOnDestroy() {\n\t\t// Cleanup all observers\n\t\tthis.observers.forEach((observer) => {\n\t\t\tif (observer) {\n\t\t\t\tobserver.disconnect();\n\t\t\t}\n\t\t});\n\t\tthis.observers = [];\n\n\t\t// Clear polling interval\n\t\tif (this.pollingInterval !== null) {\n\t\t\tclearInterval(this.pollingInterval);\n\t\t}\n\n\t\t// Clear focus timeout\n\t\tif (this.focusTimeout !== null) {\n\t\t\tclearTimeout(this.focusTimeout);\n\t\t\tthis.focusTimeout = null;\n\t\t}\n\t}\n\n\t/**\n\t * Sets up the IntersectionObserver to detect when the element enters or leaves the viewport\n\t * @private\n\t */\n\tprivate setupIntersectionObserver(): void {\n\t\tif ('IntersectionObserver' in window) {\n\t\t\tconst intersectionObserver = new IntersectionObserver(\n\t\t\t\t(entries) => {\n\t\t\t\t\tif (entries[0].isIntersecting) {\n\t\t\t\t\t\tthis.checkVisibility(true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.updateVisibility(false);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ threshold: 0.1 },\n\t\t\t);\n\n\t\t\tintersectionObserver.observe(this.elementRef.nativeElement);\n\t\t\tthis.observers.push(intersectionObserver);\n\t\t}\n\t}\n\n\t/**\n\t * Sets up MutationObserver to detect style/class changes that might affect visibility\n\t * Also monitors parent elements to detect when containers like modals appear/disappear\n\t * @private\n\t */\n\tprivate setupMutationObserver(): void {\n\t\tconst mutationObserver = new MutationObserver(() => {\n\t\t\tthis.checkVisibility(true);\n\t\t});\n\n\t\t// Watch this element\n\t\tmutationObserver.observe(this.elementRef.nativeElement, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: ['style', 'class'],\n\t\t});\n\n\t\t// Watch parent elements - important for modals, accordions, etc.\n\t\tlet parent = this.elementRef.nativeElement.parentElement;\n\t\tlet level = 0;\n\n\t\t// Watch up to 3 parent levels\n\t\twhile (parent && level < 3) {\n\t\t\tmutationObserver.observe(parent, {\n\t\t\t\tattributes: true,\n\t\t\t\tattributeFilter: ['style', 'class'],\n\t\t\t\tchildList: true,\n\t\t\t});\n\t\t\tparent = parent.parentElement;\n\t\t\tlevel++;\n\t\t}\n\n\t\tthis.observers.push(mutationObserver);\n\t}\n\n\t/**\n\t * Checks if the element is truly visible by examining computed style and dimensions\n\t * @param force Whether to force a check even if one is already in progress\n\t * @private\n\t */\n\tprivate checkVisibility(force = false): void {\n\t\tif (this.checkingVisibility && !force) return;\n\n\t\tthis.checkingVisibility = true;\n\t\tconst el = this.elementRef.nativeElement;\n\n\t\trequestAnimationFrame(() => {\n\t\t\tconst style = window.getComputedStyle(el);\n\t\t\tconst isNowVisible = el.offsetWidth > 0 && el.offsetHeight > 0 && style.visibility !== 'hidden' && style.display !== 'none';\n\n\t\t\tthis.updateVisibility(isNowVisible);\n\t\t\tthis.checkingVisibility = false;\n\t\t});\n\t}\n\n\t/**\n\t * Updates the visibility signal and triggers the effect by updating the timestamp\n\t * @param visible The new visibility state\n\t * @private\n\t */\n\tprivate updateVisibility(visible: boolean): void {\n\t\tif (this.isVisible() !== visible) {\n\t\t\tthis.isVisible.set(visible);\n\t\t\t// Always update timestamp to force the effect to run\n\t\t\tthis.changeTimestamp.set(Date.now());\n\t\t}\n\t}\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}